Adjust for changed macro names in libgpg-error master.
[gnupg.git] / g10 / keylist.c
index 82d60c2..bcbad45 100644 (file)
@@ -1,6 +1,7 @@
 /* keylist.c - Print information about OpenPGP keys
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
  *               2008, 2010, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013, 2014  Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,7 @@
  * 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 <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #ifdef HAVE_DOSISH_SYSTEM
-#include <fcntl.h>             /* for setmode() */
+# include <fcntl.h>            /* for setmode() */
 #endif
 
 #include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "status.h"
+#include "../common/status.h"
 #include "keydb.h"
 #include "photoid.h"
-#include "util.h"
-#include "ttyio.h"
+#include "../common/util.h"
+#include "../common/ttyio.h"
 #include "trustdb.h"
 #include "main.h"
-#include "i18n.h"
-#include "status.h"
+#include "../common/i18n.h"
+#include "../common/status.h"
 #include "call-agent.h"
+#include "../common/mbox-util.h"
+#include "../common/zb32.h"
+#include "tofu.h"
+#include "../common/compliance.h"
+#include "../common/pkscreening.h"
 
-static void list_all (int);
-static void list_one (strlist_t names, int secret);
+
+static void list_all (ctrl_t, int, int);
+static void list_one (ctrl_t ctrl,
+                      strlist_t names, int secret, int mark_secret);
 static void locate_one (ctrl_t ctrl, strlist_t names);
 static void print_card_serialno (const char *serialno);
 
-struct sig_stats
+struct keylist_context
 {
-  int inv_sigs;
-  int no_key;
-  int oth_err;
+  int check_sigs;  /* If set signatures shall be verified.  */
+  int good_sigs;   /* Counter used if CHECK_SIGS is set.  */
+  int inv_sigs;    /* Counter used if CHECK_SIGS is set.  */
+  int no_key;      /* Counter used if CHECK_SIGS is set.  */
+  int oth_err;     /* Counter used if CHECK_SIGS is set.  */
+  int no_validity; /* Do not show validity.  */
 };
 
+
+static void list_keyblock (ctrl_t ctrl,
+                           kbnode_t keyblock, int secret, int has_secret,
+                           int fpr, struct keylist_context *listctx);
+
+
 /* The stream used to write attribute packets to.  */
 static estream_t attrib_fp;
 
 
+/* Release resources from a keylist context.  */
+static void
+keylist_context_release (struct keylist_context *listctx)
+{
+  (void)listctx; /* Nothing to release.  */
+}
+
+
 /* List the keys.  If list is NULL, all available keys are listed.
    With LOCATE_MODE set the locate algorithm is used to find a
    key.  */
 void
 public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
 {
+#ifndef NO_TRUST_MODELS
   if (opt.with_colons)
     {
       byte trust_model, marginals, completes, cert_depth, min_cert_level;
       ulong created, nextcheck;
 
-      read_trust_options (&trust_model, &created, &nextcheck,
+      read_trust_options (ctrl, &trust_model, &created, &nextcheck,
                          &marginals, &completes, &cert_depth, &min_cert_level);
 
       es_fprintf (es_stdout, "tru:");
@@ -78,7 +103,8 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
        es_fprintf (es_stdout, "o");
       if (trust_model != opt.trust_model)
        es_fprintf (es_stdout, "t");
-      if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC)
+      if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC
+         || opt.trust_model == TM_TOFU_PGP)
        {
          if (marginals != opt.marginals_needed)
            es_fprintf (es_stdout, "m");
@@ -98,23 +124,31 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
 
       if (trust_model == TM_PGP || trust_model == TM_CLASSIC)
        es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth);
-
       es_fprintf (es_stdout, "\n");
     }
+#endif /*!NO_TRUST_MODELS*/
 
   /* We need to do the stale check right here because it might need to
      update the keyring while we already have the keyring open.  This
      is very bad for W32 because of a sharing violation. For real OSes
      it might lead to false results if we are later listing a keyring
      which is associated with the inode of a deleted file.  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
+
+#ifdef USE_TOFU
+  tofu_begin_batch_update (ctrl);
+#endif
 
   if (locate_mode)
     locate_one (ctrl, list);
   else if (!list)
-    list_all (0);
+    list_all (ctrl, 0, opt.with_secret);
   else
-    list_one (list, 0);
+    list_one (ctrl, list, 0, opt.with_secret);
+
+#ifdef USE_TOFU
+  tofu_end_batch_update (ctrl);
+#endif
 }
 
 
@@ -123,36 +157,47 @@ secret_key_list (ctrl_t ctrl, strlist_t list)
 {
   (void)ctrl;
 
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 
   if (!list)
-    list_all (1);
+    list_all (ctrl, 1, 0);
   else                         /* List by user id */
-    list_one (list, 1);
+    list_one (ctrl, list, 1, 0);
 }
 
-void
-print_seckey_info (PKT_public_key *pk)
+char *
+format_seckey_info (ctrl_t ctrl, PKT_public_key *pk)
 {
   u32 keyid[2];
   char *p;
   char pkstrbuf[PUBKEY_STRING_SIZE];
+  char *info;
 
   keyid_from_pk (pk, keyid);
-  p = get_user_id_native (keyid);
+  p = get_user_id_native (ctrl, keyid);
+
+  info = xtryasprintf ("sec  %s/%s %s %s",
+                       pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+                       keystr (keyid), datestr_from_pk (pk), p);
 
-  tty_printf ("\nsec  %s/%s %s %s\n",
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-             keystr (keyid), datestr_from_pk (pk), p);
+  xfree (p);
+
+  return info;
+}
 
+void
+print_seckey_info (ctrl_t ctrl, PKT_public_key *pk)
+{
+  char *p = format_seckey_info (ctrl, pk);
+  tty_printf ("\n%s\n", p);
   xfree (p);
 }
 
 /* Print information about the public key.  With FP passed as NULL,
-   the tty output interface is used, otherwise output is directted to
+   the tty output interface is used, otherwise output is directed to
    the given stream.  */
 void
-print_pubkey_info (estream_t fp, PKT_public_key * pk)
+print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk)
 {
   u32 keyid[2];
   char *p;
@@ -165,11 +210,12 @@ print_pubkey_info (estream_t fp, PKT_public_key * pk)
   if (pk->user_id)
     p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0);
   else
-    p = get_user_id_native (keyid);
+    p = get_user_id_native (ctrl, keyid);
 
   if (fp)
     tty_printf ("\n");
-  tty_fprintf (fp, "pub  %s/%s %s %s\n",
+  tty_fprintf (fp, "%s  %s/%s %s %s\n",
+               pk->flags.primary? "pub":"sub",
                pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
                keystr (keyid), datestr_from_pk (pk), p);
   xfree (p);
@@ -187,6 +233,7 @@ print_card_key_info (estream_t fp, kbnode_t keyblock)
   char *serialno;
   int s2k_char;
   char pkstrbuf[PUBKEY_STRING_SIZE];
+  int indent;
 
   for (node = keyblock; node; node = node->next)
     {
@@ -203,23 +250,23 @@ print_card_key_info (estream_t fp, kbnode_t keyblock)
               log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
               s2k_char = '?';
             }
-          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
+          else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             s2k_char = serialno? '>':' ';
           else
             s2k_char = '#';  /* Key not found.  */
 
-          tty_fprintf (fp, "%s%c  %s/%s  ",
+          tty_fprintf (fp, "%s%c  %s/%s  %n",
                        node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb",
                        s2k_char,
                        pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-                       keystr_from_pk (pk));
+                       keystr_from_pk (pk),
+                       &indent);
           tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk));
           tty_fprintf (fp, "  ");
           tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
           if (serialno)
             {
-              tty_fprintf (fp, "\n                      ");
-              tty_fprintf (fp, _("card-no: "));
+              tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: "));
               if (strlen (serialno) == 32
                   && !strncmp (serialno, "D27600012401", 12))
                 {
@@ -259,6 +306,7 @@ status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
 
 
 /* Print a policy URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
  *   0 - print to stdout.
  *   1 - use log_info and emit status messages.
  *   2 - emit only status messages.
@@ -269,50 +317,48 @@ show_policy_url (PKT_signature * sig, int indent, int mode)
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
   while ((p =
          enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit)))
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', fp);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical signature policy: ");
          else
            str = _("Signature policy: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (fp, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (fp, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        write_status_buffer (STATUS_POLICY_URL, p, len, 0);
     }
 }
 
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-*/
-/* TODO: use this */
+/* Print a keyserver URL.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ */
 void
 show_keyserver_url (PKT_signature * sig, int indent, int mode)
 {
   const byte *p;
   size_t len;
   int seq = 0, crit;
-  estream_t fp = mode ? log_get_stream () : es_stdout;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
 
   while ((p =
          enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq,
@@ -320,44 +366,44 @@ show_keyserver_url (PKT_signature * sig, int indent, int mode)
     {
       if (mode != 2)
        {
-         int i;
          const char *str;
 
-         for (i = 0; i < indent; i++)
-           es_putc (' ', es_stdout);
+          tty_fprintf (fp, "%*s", indent, "");
 
          if (crit)
            str = _("Critical preferred keyserver: ");
          else
            str = _("Preferred keyserver: ");
-         if (mode)
+         if (mode > 0)
            log_info ("%s", str);
          else
-           es_fprintf (es_stdout, "%s", str);
-         print_utf8_buffer (fp, p, len);
-         es_fprintf (fp, "\n");
+           tty_fprintf (fp, "%s", str);
+         tty_print_utf8_string2 (fp, p, len, 0);
+         tty_fprintf (fp, "\n");
        }
 
-      if (mode)
+      if (mode > 0)
        status_one_subpacket (SIGSUBPKT_PREF_KS, len,
                              (crit ? 0x02 : 0) | 0x01, p);
     }
 }
 
-/*
-  mode=0 for stdout.
-  mode=1 for log_info + status messages
-  mode=2 for status messages only
-
-  Defined bits in WHICH:
-    1 == standard notations
-    2 == user notations
-*/
+
+/* Print notation data.  Allowed values for MODE are:
+ *  -1 - print to the TTY
+ *   0 - print to stdout.
+ *   1 - use log_info and emit status messages.
+ *   2 - emit only status messages.
+ *
+ * Defined bits in WHICH:
+ *   1 - standard notations
+ *   2 - user notations
+ */
 void
 show_notation (PKT_signature * sig, int indent, int mode, int which)
 {
-  estream_t fp = mode ? log_get_stream () : es_stdout;
-  struct notation *nd, *notations;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
+  notation_t nd, notations;
 
   if (which == 0)
     which = 3;
@@ -373,32 +419,39 @@ show_notation (PKT_signature * sig, int indent, int mode, int which)
 
          if ((which & 1 && !has_at) || (which & 2 && has_at))
            {
-             int i;
              const char *str;
 
-             for (i = 0; i < indent; i++)
-               es_putc (' ', es_stdout);
+              tty_fprintf (fp, "%*s", indent, "");
 
              if (nd->flags.critical)
                str = _("Critical signature notation: ");
              else
                str = _("Signature notation: ");
-             if (mode)
+             if (mode > 0)
                log_info ("%s", str);
              else
-               es_fprintf (es_stdout, "%s", str);
+               tty_fprintf (fp, "%s", str);
              /* This is all UTF8 */
-             print_utf8_buffer (fp, nd->name, strlen (nd->name));
-             es_fprintf (fp, "=");
-             print_utf8_buffer (fp, nd->value, strlen (nd->value));
-             es_fprintf (fp, "\n");
+             tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0);
+             tty_fprintf (fp, "=");
+             tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0);
+              /* (We need to use log_printf so that the next call to a
+                  log function does not insert an extra LF.)  */
+              if (mode > 0)
+                log_printf ("\n");
+              else
+                tty_fprintf (fp, "\n");
            }
        }
 
-      if (mode)
+      if (mode > 0)
        {
          write_status_buffer (STATUS_NOTATION_NAME,
                               nd->name, strlen (nd->name), 0);
+          if (nd->flags.critical || nd->flags.human)
+            write_status_text (STATUS_NOTATION_FLAGS,
+                               nd->flags.critical && nd->flags.human? "1 1" :
+                               nd->flags.critical? "1 0" : "0 1");
          write_status_buffer (STATUS_NOTATION_DATA,
                               nd->value, strlen (nd->value), 50);
        }
@@ -407,44 +460,63 @@ show_notation (PKT_signature * sig, int indent, int mode, int which)
   free_notation (notations);
 }
 
+
 static void
-print_signature_stats (struct sig_stats *s)
+print_signature_stats (struct keylist_context *s)
 {
-  if (s->inv_sigs == 1)
-    tty_printf (_("1 bad signature\n"));
-  else if (s->inv_sigs)
-    tty_printf (_("%d bad signatures\n"), s->inv_sigs);
-  if (s->no_key == 1)
-    tty_printf (_("1 signature not checked due to a missing key\n"));
-  else if (s->no_key)
-    tty_printf (_("%d signatures not checked due to missing keys\n"),
-               s->no_key);
-  if (s->oth_err == 1)
-    tty_printf (_("1 signature not checked due to an error\n"));
-  else if (s->oth_err)
-    tty_printf (_("%d signatures not checked due to errors\n"), s->oth_err);
+  if (!s->check_sigs)
+    return;  /* Signature checking was not requested.  */
+
+  /* Better flush stdout so that the stats are always printed after
+   * the output.  */
+  es_fflush (es_stdout);
+
+  if (s->good_sigs)
+    log_info (ngettext("%d good signature\n",
+                       "%d good signatures\n", s->good_sigs), s->good_sigs);
+
+  if (s->inv_sigs)
+    log_info (ngettext("%d bad signature\n",
+                       "%d bad signatures\n", s->inv_sigs), s->inv_sigs);
+
+  if (s->no_key)
+    log_info (ngettext("%d signature not checked due to a missing key\n",
+                       "%d signatures not checked due to missing keys\n",
+                       s->no_key), s->no_key);
+
+  if (s->oth_err)
+    log_info (ngettext("%d signature not checked due to an error\n",
+                       "%d signatures not checked due to errors\n",
+                       s->oth_err), s->oth_err);
 }
 
+
+/* List all keys.  If SECRET is true only secret keys are listed.  If
+   MARK_SECRET is true secret keys are indicated in a public key
+   listing.  */
 static void
-list_all (int secret)
+list_all (ctrl_t ctrl, int secret, int mark_secret)
 {
   KEYDB_HANDLE hd;
   KBNODE keyblock = NULL;
   int rc = 0;
+  int any_secret;
   const char *lastresname, *resname;
-  struct sig_stats stats;
+  struct keylist_context listctx;
 
-  memset (&stats, 0, sizeof (stats));
+  memset (&listctx, 0, sizeof (listctx));
+  if (opt.check_sigs)
+    listctx.check_sigs = 1;
 
   hd = keydb_new ();
   if (!hd)
-    rc = gpg_error (GPG_ERR_GENERAL);
+    rc = gpg_error_from_syserror ();
   else
     rc = keydb_search_first (hd);
   if (rc)
     {
       if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
-       log_error ("keydb_search_first failed: %s\n", g10_errstr (rc));
+       log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc));
       goto leave;
     }
 
@@ -454,10 +526,18 @@ list_all (int secret)
       rc = keydb_get_keyblock (hd, &keyblock);
       if (rc)
        {
-         log_error ("keydb_get_keyblock failed: %s\n", g10_errstr (rc));
+          if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY)
+            continue;  /* Skip legacy keys.  */
+         log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
          goto leave;
        }
-      if (secret && agent_probe_any_secret_key (NULL, keyblock))
+
+      if (secret || mark_secret)
+        any_secret = !agent_probe_any_secret_key (NULL, keyblock);
+      else
+        any_secret = 0;
+
+      if (secret && !any_secret)
         ; /* Secret key listing requested but this isn't one.  */
       else
         {
@@ -475,28 +555,35 @@ list_all (int secret)
                   lastresname = resname;
                 }
             }
-          merge_keys_and_selfsig (keyblock);
-          list_keyblock (keyblock, secret, opt.fingerprint,
-                         opt.check_sigs ? &stats : NULL);
+          merge_keys_and_selfsig (ctrl, keyblock);
+          list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint,
+                         &listctx);
         }
       release_kbnode (keyblock);
       keyblock = NULL;
     }
   while (!(rc = keydb_search_next (hd)));
+  es_fflush (es_stdout);
   if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
-    log_error ("keydb_search_next failed: %s\n", g10_errstr (rc));
+    log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc));
+  if (keydb_get_skipped_counter (hd))
+    log_info (ngettext("Warning: %lu key skipped due to its large size\n",
+                       "Warning: %lu keys skipped due to their large sizes\n",
+                       keydb_get_skipped_counter (hd)),
+              keydb_get_skipped_counter (hd));
 
   if (opt.check_sigs && !opt.with_colons)
-    print_signature_stats (&stats);
+    print_signature_stats (&listctx);
 
-leave:
+ leave:
+  keylist_context_release (&listctx);
   release_kbnode (keyblock);
   keydb_release (hd);
 }
 
 
 static void
-list_one (strlist_t names, int secret)
+list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret)
 {
   int rc = 0;
   KBNODE keyblock = NULL;
@@ -504,12 +591,14 @@ list_one (strlist_t names, int secret)
   const char *resname;
   const char *keyring_str = _("Keyring");
   int i;
-  struct sig_stats stats;
+  struct keylist_context listctx;
 
-  memset (&stats, 0, sizeof (stats));
+  memset (&listctx, 0, sizeof (listctx));
+  if (!secret && opt.check_sigs)
+    listctx.check_sigs = 1;
 
   /* fixme: using the bynames function has the disadvantage that we
-   * don't know wether one of the names given was not found.  OTOH,
+   * don't know whether one of the names given was not found.  OTOH,
    * this function has the advantage to list the names in the
    * sequence as defined by the keyDB and does not duplicate
    * outputs.  A solution could be do test whether all given have
@@ -517,11 +606,11 @@ list_one (strlist_t names, int secret)
    * functions) or to have the search function return indicators for
    * found names.  Yet another way is to use the keydb search
    * facilities directly. */
-  rc = getkey_bynames (&ctx, NULL, names, secret, &keyblock);
+  rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock);
   if (rc)
     {
-      log_error ("error reading key: %s\n", g10_errstr (rc));
-      get_pubkey_end (ctx);
+      log_error ("error reading key: %s\n", gpg_strerror (rc));
+      getkey_end (ctrl, ctx);
       return;
     }
 
@@ -535,15 +624,17 @@ list_one (strlist_t names, int secret)
             es_putc ('-', es_stdout);
           es_putc ('\n', es_stdout);
         }
-      list_keyblock (keyblock, secret, opt.fingerprint,
-                     (!secret && opt.check_sigs)? &stats : NULL);
+      list_keyblock (ctrl,
+                     keyblock, secret, mark_secret, opt.fingerprint, &listctx);
       release_kbnode (keyblock);
     }
-  while (!getkey_next (ctx, NULL, &keyblock));
-  getkey_end (ctx);
+  while (!getkey_next (ctrl, ctx, NULL, &keyblock));
+  getkey_end (ctrl, ctx);
 
   if (opt.check_sigs && !opt.with_colons)
-    print_signature_stats (&stats);
+    print_signature_stats (&listctx);
+
+  keylist_context_release (&listctx);
 }
 
 
@@ -554,34 +645,40 @@ locate_one (ctrl_t ctrl, strlist_t names)
   strlist_t sl;
   GETKEY_CTX ctx = NULL;
   KBNODE keyblock = NULL;
-  struct sig_stats stats;
+  struct keylist_context listctx;
 
-  memset (&stats, 0, sizeof (stats));
+  memset (&listctx, 0, sizeof (listctx));
+  if (opt.check_sigs)
+    listctx.check_sigs = 1;
 
   for (sl = names; sl; sl = sl->next)
     {
-      rc = get_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, NULL, 1, 0);
+      rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0);
       if (rc)
        {
          if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
-           log_error ("error reading key: %s\n", g10_errstr (rc));
+           log_error ("error reading key: %s\n", gpg_strerror (rc));
+          else if (opt.verbose)
+            log_info (_("key \"%s\" not found: %s\n"),
+                      sl->d, gpg_strerror (rc));
        }
       else
        {
          do
            {
-             list_keyblock (keyblock, 0, opt.fingerprint,
-                            opt.check_sigs ? &stats : NULL);
+             list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx);
              release_kbnode (keyblock);
            }
-         while (ctx && !get_pubkey_next (ctx, NULL, &keyblock));
-         get_pubkey_end (ctx);
+         while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock));
+         getkey_end (ctrl, ctx);
          ctx = NULL;
        }
     }
 
   if (opt.check_sigs && !opt.with_colons)
-    print_signature_stats (&stats);
+    print_signature_stats (&listctx);
+
+  keylist_context_release (&listctx);
 }
 
 
@@ -600,8 +697,39 @@ print_key_data (PKT_public_key * pk)
     }
 }
 
+
+/* Various public key screenings.  (Right now just ROCA).  With
+ * COLON_MODE set the output is formatted for use in the compliance
+ * field of a colon listing.
+ */
 static void
-print_capabilities (PKT_public_key *pk, KBNODE keyblock)
+print_pk_screening (PKT_public_key *pk, int colon_mode)
+{
+  gpg_error_t err;
+
+  if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo))
+    {
+      err = screen_key_for_roca (pk->pkey[0]);
+      if (!err)
+        ;
+      else if (gpg_err_code (err) == GPG_ERR_TRUE)
+        {
+          if (colon_mode)
+            es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001);
+          else
+            es_fprintf (es_stdout,
+                        "      Screening: ROCA vulnerability detected\n");
+        }
+      else if (!colon_mode)
+        es_fprintf (es_stdout, "      Screening: [ROCA check failed: %s]\n",
+                    gpg_strerror (err));
+    }
+
+}
+
+
+static void
+print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock)
 {
   unsigned int use = pk->pubkey_usage;
   int c_printed = 0;
@@ -709,7 +837,7 @@ print_subpackets_colon (PKT_signature * sig)
 {
   byte *i;
 
-  assert (opt.show_subpackets);
+  log_assert (opt.show_subpackets);
 
   for (i = opt.show_subpackets; *i; i++)
     {
@@ -758,9 +886,8 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
                   (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1,
                   uid->numattribs, (ulong) uid->created,
                   (ulong) uid->expiredate,
-                  ((uid->is_primary ? 0x01 : 0) | (uid->
-                                                   is_revoked ? 0x02 : 0) |
-                   (uid->is_expired ? 0x04 : 0)));
+                  ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) |
+                   (uid->flags.expired ? 0x04 : 0)));
          write_status_text (STATUS_ATTRIBUTE, buf);
        }
 
@@ -771,18 +898,16 @@ dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
 
 
 static void
-list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
+list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
+                     struct keylist_context *listctx)
 {
   int rc;
   KBNODE kbctx;
   KBNODE node;
   PKT_public_key *pk;
-  struct sig_stats *stats = opaque;
   int skip_sigs = 0;
-  int s2k_char;
   char *hexgrip = NULL;
   char *serialno = NULL;
-  char pkstrbuf[PUBKEY_STRING_SIZE];
 
   /* Get the keyid from the keyblock.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
@@ -804,68 +929,21 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
 
   if (secret)
     {
-      if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-        s2k_char = serialno? '>':' ';
+      /* Encode some info about the secret key in SECRET.  */
+      if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+        secret = serialno? 3 : 1;
       else
-        s2k_char = '#';  /* Key not found.  */
+        secret = 2;  /* Key not found.  */
     }
-  else
-    s2k_char = ' ';
 
-  check_trustdb_stale ();
+  if (!listctx->no_validity)
+    check_trustdb_stale (ctrl);
 
-
-  es_fprintf (es_stdout, "%s%c  %s/%s %s",
-              secret? "sec":"pub",
-              s2k_char,
-              pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-              keystr_from_pk (pk), datestr_from_pk (pk));
-
-  if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
-      || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
-      || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
-    {
-      char *curve = openpgp_oid_to_str (pk->pkey[0]);
-      const char *name = openpgp_oid_to_curve (curve);
-      if (!*name || *name == '?')
-        name = curve;
-      es_fprintf (es_stdout, " %s", name);
-      xfree (curve);
-    }
-
-  if (pk->flags.revoked)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-  else if (pk->has_expired)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-  else if (pk->expiredate)
-    {
-      es_fprintf (es_stdout, " [");
-      es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk));
-      es_fprintf (es_stdout, "]");
-    }
-
-#if 0
-  /* I need to think about this some more.  It's easy enough to
-     include, but it looks sort of confusing in the listing... */
-  if (opt.list_options & LIST_SHOW_VALIDITY)
-    {
-      int validity = get_validity (pk, NULL);
-      es_fprintf (es_stdout, " [%s]", trust_value_to_string (validity));
-    }
-#endif
-
-  es_fprintf (es_stdout, "\n");
+  /* Print the "pub" line and in KF_NONE mode the fingerprint.  */
+  print_key_line (ctrl, es_stdout, pk, secret);
 
   if (fpr)
-    print_fingerprint (pk, 0);
+    print_fingerprint (ctrl, NULL, pk, 0);
 
   if (opt.with_keygrip && hexgrip)
     es_fprintf (es_stdout, "      Keygrip = %s\n", hexgrip);
@@ -876,13 +954,33 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
   if (opt.with_key_data)
     print_key_data (pk);
 
+  if (opt.with_key_screening)
+    print_pk_screening (pk, 0);
+
+  if (opt.with_key_origin
+      && (pk->keyorg || pk->keyupdate || pk->updateurl))
+    {
+      char updatestr[MK_DATESTR_SIZE];
+
+      es_fprintf (es_stdout, "      origin=%s last=%s %s",
+                  key_origin_string (pk->keyorg),
+                  mk_datestr (updatestr, sizeof updatestr, pk->keyupdate),
+                  pk->updateurl? "url=":"");
+      if (pk->updateurl)
+        print_utf8_string (es_stdout, pk->updateurl);
+      es_putc ('\n', es_stdout);
+    }
+
+
   for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
     {
-      if (node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode)
+      if (node->pkt->pkttype == PKT_USER_ID)
        {
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int indent;
+          int kl = opt.keyid_format == KF_NONE? 10 : keystrlen ();
 
-         if (pk && (uid->is_expired || uid->is_revoked)
+         if ((uid->flags.expired || uid->flags.revoked)
              && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
            {
              skip_sigs = 1;
@@ -894,30 +992,69 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
          if (attrib_fp && uid->attrib_data != NULL)
            dump_attribs (uid, pk);
 
-         if ((uid->is_revoked || uid->is_expired)
-             || ((opt.list_options & LIST_SHOW_UID_VALIDITY) && pk))
+         if ((uid->flags.revoked || uid->flags.expired)
+             || ((opt.list_options & LIST_SHOW_UID_VALIDITY)
+                  && !listctx->no_validity))
            {
              const char *validity;
-             int indent;
-
-             validity = uid_trust_string_fixed (pk, uid);
-             indent =
-               (keystrlen () + 9) -
-               atoi (uid_trust_string_fixed (NULL, NULL));
 
+             validity = uid_trust_string_fixed (ctrl, pk, uid);
+             indent = ((kl + (opt.legacy_list_mode? 9:11))
+                        - atoi (uid_trust_string_fixed (ctrl, NULL, NULL)));
              if (indent < 0 || indent > 40)
                indent = 0;
 
              es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
            }
          else
-           es_fprintf (es_stdout, "uid%*s", (int) keystrlen () + 10, "");
+            {
+              indent = kl + (opt.legacy_list_mode? 10:12);
+              es_fprintf (es_stdout, "uid%*s", indent, "");
+            }
 
          print_utf8_buffer (es_stdout, uid->name, uid->len);
          es_putc ('\n', es_stdout);
 
+          if (opt.with_wkd_hash)
+            {
+              char *mbox, *hash, *p;
+              char hashbuf[32];
+
+              mbox = mailbox_from_userid (uid->name);
+              if (mbox && (p = strchr (mbox, '@')))
+                {
+                  *p++ = 0;
+                  gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf,
+                                       mbox, strlen (mbox));
+                  hash = zb32_encode (hashbuf, 8*20);
+                  if (hash)
+                    {
+                      es_fprintf (es_stdout, "   %*s%s@%s\n",
+                                  indent, "", hash, p);
+                      xfree (hash);
+                    }
+                }
+              xfree (mbox);
+            }
+
+          if (opt.with_key_origin
+              && (uid->keyorg || uid->keyupdate || uid->updateurl))
+            {
+              char updatestr[MK_DATESTR_SIZE];
+
+              es_fprintf (es_stdout, "   %*sorigin=%s last=%s %s",
+                          indent, "",
+                          key_origin_string (uid->keyorg),
+                          mk_datestr (updatestr, sizeof updatestr,
+                                      uid->keyupdate),
+                          uid->updateurl? "url=":"");
+              if (uid->updateurl)
+                print_utf8_string (es_stdout, uid->updateurl);
+              es_putc ('\n', es_stdout);
+            }
+
          if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
-           show_photos (uid->attribs, uid->numattribs, pk, uid);
+           show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid);
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
@@ -943,54 +1080,17 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
             }
           if (secret)
             {
-              if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
-                s2k_char = serialno? '>':' ';
+              if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
+                secret = serialno? 3 : 1;
               else
-                s2k_char = '#';  /* Key not found.  */
+                secret = 2;  /* Key not found.  */
             }
-          else
-            s2k_char = ' ';
-
-         es_fprintf (es_stdout, "%s%c  %s/%s %s",
-                  secret? "ssb":"sub",
-                  s2k_char,
-                  pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
-                 keystr_from_pk (pk2), datestr_from_pk (pk2));
 
-          if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
-              || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
-              || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
+          /* Print the "sub" line.  */
+          print_key_line (ctrl, es_stdout, pk2, secret);
+         if (fpr > 1 || opt.with_subkey_fingerprint)
             {
-              char *curve = openpgp_oid_to_str (pk2->pkey[0]);
-              const char *name = openpgp_oid_to_curve (curve);
-              if (!*name || *name == '?')
-                name = curve;
-              es_fprintf (es_stdout, " %s", name);
-              xfree (curve);
-            }
-
-         if (pk2->flags.revoked)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->has_expired)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         else if (pk2->expiredate)
-           {
-             es_fprintf (es_stdout, " [");
-             es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk2));
-             es_fprintf (es_stdout, "]");
-           }
-         es_putc ('\n', es_stdout);
-         if (fpr > 1)
-            {
-              print_fingerprint (pk2, 0);
+              print_fingerprint (ctrl, NULL, pk2, 0);
               if (serialno)
                 print_card_serialno (serialno);
             }
@@ -998,6 +1098,8 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
             es_fprintf (es_stdout, "      Keygrip = %s\n", hexgrip);
          if (opt.with_key_data)
            print_key_data (pk2);
+          if (opt.with_key_screening)
+            print_pk_screening (pk2, 0);
        }
       else if (opt.list_sigs
               && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs)
@@ -1006,24 +1108,25 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
          int sigrc;
          char *sigstr;
 
-         if (stats)
+         if (listctx->check_sigs)
            {
-             rc = check_key_signature (keyblock, node, NULL);
+             rc = check_key_signature (ctrl, keyblock, node, NULL);
              switch (gpg_err_code (rc))
                {
                case 0:
+                 listctx->good_sigs++;
                  sigrc = '!';
                  break;
                case GPG_ERR_BAD_SIGNATURE:
-                 stats->inv_sigs++;
+                 listctx->inv_sigs++;
                  sigrc = '-';
                  break;
                case GPG_ERR_NO_PUBKEY:
                case GPG_ERR_UNUSABLE_PUBKEY:
-                 stats->no_key++;
+                 listctx->no_key++;
                  continue;
                default:
-                 stats->oth_err++;
+                 listctx->oth_err++;
                  sigrc = '%';
                  break;
                }
@@ -1073,13 +1176,13 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
            es_fprintf (es_stdout, " %s", expirestr_from_sig (sig));
          es_fprintf (es_stdout, "  ");
          if (sigrc == '%')
-           es_fprintf (es_stdout, "[%s] ", g10_errstr (rc));
+           es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
          else if (sigrc == '?')
            ;
          else if (!opt.fast_list_mode)
            {
              size_t n;
-             char *p = get_user_id (sig->keyid, &n);
+             char *p = get_user_id (ctrl, sig->keyid, &n);
              print_utf8_buffer (es_stdout, p, n);
              xfree (p);
            }
@@ -1111,7 +1214,7 @@ list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
 }
 
 void
-print_revokers (PKT_public_key * pk)
+print_revokers (estream_t fp, PKT_public_key * pk)
 {
   /* print the revoker record */
   if (!pk->revkey && pk->numrevkeys)
@@ -1124,18 +1227,55 @@ print_revokers (PKT_public_key * pk)
        {
          byte *p;
 
-         es_fprintf (es_stdout, "rvk:::%d::::::", pk->revkey[i].algid);
+         es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid);
          p = pk->revkey[i].fpr;
          for (j = 0; j < 20; j++, p++)
-           es_fprintf (es_stdout, "%02X", *p);
-         es_fprintf (es_stdout, ":%02x%s:\n", pk->revkey[i].class,
-                 (pk->revkey[i].class & 0x40) ? "s" : "");
+           es_fprintf (fp, "%02X", *p);
+         es_fprintf (fp, ":%02x%s:\n",
+                      pk->revkey[i].class,
+                      (pk->revkey[i].class & 0x40) ? "s" : "");
        }
     }
 }
 
+
+/* Print the compliance flags to field 18.  PK is the public key.
+ * KEYLENGTH is the length of the key in bits and CURVENAME is either
+ * NULL or the name of the curve.  The latter two args are here
+ * merely because the caller has already computed them.  */
+static void
+print_compliance_flags (PKT_public_key *pk,
+                        unsigned int keylength, const char *curvename)
+{
+  int any = 0;
+
+  if (!keylength)
+    keylength = nbits_from_pk (pk);
+
+  if (pk->version == 5)
+    {
+      es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout);
+      any++;
+    }
+  if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey,
+                            keylength, curvename))
+    {
+      es_fprintf (es_stdout, any ? " %s" : "%s",
+                 gnupg_status_compliance_flag (CO_DE_VS));
+      any++;
+    }
+
+  if (opt.with_key_screening)
+    print_pk_screening (pk, 1+any);
+}
+
+
+/* List a key in colon mode.  If SECRET is true this is a secret key
+   record (i.e. requested via --list-secret-key).  If HAS_SECRET a
+   secret key is available even if SECRET is not set.  */
 static void
-list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
+list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
+                     int secret, int has_secret)
 {
   int rc;
   KBNODE kbctx;
@@ -1143,12 +1283,17 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
   PKT_public_key *pk;
   u32 keyid[2];
   int trustletter = 0;
+  int trustletter_print;
+  int ownertrust_print;
   int ulti_hack = 0;
   int i;
-  char *p;
-  char *hexgrip = NULL;
+  char *hexgrip_buffer = NULL;
+  const char *hexgrip = NULL;
   char *serialno = NULL;
   int stubkey;
+  unsigned int keylength;
+  char *curve = NULL;
+  const char *curvename = NULL;
 
   /* Get the keyid from the keyblock.  */
   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
@@ -1160,55 +1305,71 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
     }
 
   pk = node->pkt->pkt.public_key;
-  if (secret || opt.with_keygrip || opt.with_key_data)
+  if (secret || has_secret || opt.with_keygrip || opt.with_key_data)
     {
-      rc = hexkeygrip_from_pk (pk, &hexgrip);
+      rc = hexkeygrip_from_pk (pk, &hexgrip_buffer);
       if (rc)
         log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
+      /* In the error case we print an empty string so that we have a
+       * "grp" record for each and subkey - even if it is empty.  This
+       * may help to prevent sync problems.  */
+      hexgrip = hexgrip_buffer? hexgrip_buffer : "";
     }
   stubkey = 0;
-  if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno))
+  if ((secret || has_secret)
+      && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
     stubkey = 1;  /* Key not found.  */
 
   keyid_from_pk (pk, keyid);
-  es_fputs (secret? "sec:":"pub:", es_stdout);
   if (!pk->flags.valid)
-    es_putc ('i', es_stdout);
+    trustletter_print = 'i';
   else if (pk->flags.revoked)
-    es_putc ('r', es_stdout);
+    trustletter_print = 'r';
   else if (pk->has_expired)
-    es_putc ('e', es_stdout);
+    trustletter_print = 'e';
   else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
-    ;
+    trustletter_print = 0;
   else
     {
-      trustletter = get_validity_info (pk, NULL);
+      trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
       if (trustletter == 'u')
         ulti_hack = 1;
-      es_putc (trustletter, es_stdout);
+      trustletter_print = trustletter;
     }
 
+  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
+    ownertrust_print = get_ownertrust_info (ctrl, pk, 0);
+  else
+    ownertrust_print = 0;
+
+  keylength = nbits_from_pk (pk);
+
+  es_fputs (secret? "sec:":"pub:", es_stdout);
+  if (trustletter_print)
+    es_putc (trustletter_print, es_stdout);
   es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
-          nbits_from_pk (pk),
-          pk->pubkey_algo,
-          (ulong) keyid[0], (ulong) keyid[1],
-          colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
+              keylength,
+              pk->pubkey_algo,
+              (ulong) keyid[0], (ulong) keyid[1],
+              colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
 
-  if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
-    es_putc (get_ownertrust_info (pk), es_stdout);
+  if (ownertrust_print)
+    es_putc (ownertrust_print, es_stdout);
   es_putc (':', es_stdout);
 
   es_putc (':', es_stdout);
   es_putc (':', es_stdout);
-  print_capabilities (pk, keyblock);
+  print_capabilities (ctrl, pk, keyblock);
   es_putc (':', es_stdout);            /* End of field 13. */
   es_putc (':', es_stdout);            /* End of field 14. */
-  if (secret)
+  if (secret || has_secret)
     {
       if (stubkey)
        es_putc ('#', es_stdout);
       else if (serialno)
         es_fputs (serialno, es_stdout);
+      else if (has_secret)
+        es_putc ('+', es_stdout);
     }
   es_putc (':', es_stdout);            /* End of field 15. */
   es_putc (':', es_stdout);            /* End of field 16. */
@@ -1216,56 +1377,57 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
       || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
       || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
     {
-      char *curve = openpgp_oid_to_str (pk->pkey[0]);
-      const char *name = openpgp_oid_to_curve (curve);
-      if (!*name || *name == '?')
-        name = curve;
-      es_fputs (name, es_stdout);
-      xfree (curve);
+      curve = openpgp_oid_to_str (pk->pkey[0]);
+      curvename = openpgp_oid_to_curve (curve, 0);
+      if (!curvename)
+        curvename = curve;
+      es_fputs (curvename, es_stdout);
     }
   es_putc (':', es_stdout);            /* End of field 17. */
+  print_compliance_flags (pk, keylength, curvename);
+  es_putc (':', es_stdout);            /* End of field 18 (compliance). */
+  if (pk->keyupdate)
+    es_fputs (colon_strtime (pk->keyupdate), es_stdout);
+  es_putc (':', es_stdout);            /* End of field 19 (last_update). */
+  es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":"");
+  if (pk->updateurl)
+    es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl),
+                        ":", NULL);
+  es_putc (':', es_stdout);            /* End of field 20 (origin). */
   es_putc ('\n', es_stdout);
 
-  print_revokers (pk);
-  if (fpr)
-    print_fingerprint (pk, 0);
-  if (opt.with_key_data || opt.with_keygrip)
-    {
-      if (hexgrip)
-        es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-      if (opt.with_key_data)
-        print_key_data (pk);
-    }
+  print_revokers (es_stdout, pk);
+  print_fingerprint (ctrl, NULL, pk, 0);
+  if (hexgrip)
+    es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+  if (opt.with_key_data)
+    print_key_data (pk);
 
   for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
     {
-      if (node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode)
+      if (node->pkt->pkttype == PKT_USER_ID)
        {
-         char *str;
          PKT_user_id *uid = node->pkt->pkt.user_id;
+          int uid_validity;
 
-         if (attrib_fp && node->pkt->pkt.user_id->attrib_data != NULL)
-           dump_attribs (node->pkt->pkt.user_id, pk);
-         /*
-          * Fixme: We need a valid flag here too
-          */
-         str = uid->attrib_data ? "uat" : "uid";
-         if (uid->is_revoked)
-           es_fprintf (es_stdout, "%s:r::::", str);
-         else if (uid->is_expired)
-           es_fprintf (es_stdout, "%s:e::::", str);
+         if (attrib_fp && uid->attrib_data != NULL)
+           dump_attribs (uid, pk);
+
+         if (uid->flags.revoked)
+           uid_validity = 'r';
+         else if (uid->flags.expired)
+           uid_validity = 'e';
          else if (opt.no_expensive_trust_checks)
-           es_fprintf (es_stdout, "%s:::::", str);
-         else
-           {
-             int uid_validity;
+           uid_validity = 0;
+         else if (ulti_hack)
+            uid_validity = 'u';
+          else
+            uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
 
-             if (pk && !ulti_hack)
-               uid_validity = get_validity_info (pk, uid);
-             else
-               uid_validity = 'u';
-             es_fprintf (es_stdout, "%s:%c::::", str, uid_validity);
-           }
+          es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
+          if (uid_validity)
+            es_putc (uid_validity, es_stdout);
+          es_fputs ("::::", es_stdout);
 
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->created));
          es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate));
@@ -1281,26 +1443,47 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
            es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
          else
            es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
-         es_putc (':', es_stdout);
+         es_fputs (":::::::::", es_stdout);
+          if (uid->keyupdate)
+            es_fputs (colon_strtime (uid->keyupdate), es_stdout);
+          es_putc (':', es_stdout);    /* End of field 19 (last_update). */
+          es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":"");
+          if (uid->updateurl)
+            es_write_sanitized (es_stdout,
+                                uid->updateurl, strlen (uid->updateurl),
+                                ":", NULL);
+          es_putc (':', es_stdout);    /* End of field 20 (origin). */
          es_putc ('\n', es_stdout);
+#ifdef USE_TOFU
+         if (!uid->attrib_data && opt.with_tofu_info
+              && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP))
+           {
+              /* Print a "tfs" record.  */
+              tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name);
+           }
+#endif /*USE_TOFU*/
        }
       else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
          u32 keyid2[2];
          PKT_public_key *pk2;
+          int need_hexgrip = !!hexgrip;
 
           pk2 = node->pkt->pkt.public_key;
-          xfree (hexgrip); hexgrip = NULL;
+          xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL;
           xfree (serialno); serialno = NULL;
-          if (secret || opt.with_keygrip || opt.with_key_data)
+          if (need_hexgrip
+              || secret || has_secret || opt.with_keygrip || opt.with_key_data)
             {
-              rc = hexkeygrip_from_pk (pk2, &hexgrip);
+              rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer);
               if (rc)
                 log_error ("error computing a keygrip: %s\n",
                            gpg_strerror (rc));
+              hexgrip = hexgrip_buffer? hexgrip_buffer : "";
             }
           stubkey = 0;
-          if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno))
+          if ((secret||has_secret)
+              && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
             stubkey = 1;  /* Key not found.  */
 
          keyid_from_pk (pk2, keyid2);
@@ -1319,47 +1502,47 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
              if (trustletter)
                es_fprintf (es_stdout, "%c", trustletter);
            }
+          keylength = nbits_from_pk (pk2);
          es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
-                 nbits_from_pk (pk2),
-                 pk2->pubkey_algo,
-                 (ulong) keyid2[0], (ulong) keyid2[1],
-                 colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)
-                 /* fixme: add LID and ownertrust here */
-           );
-         print_capabilities (pk2, NULL);
+                      keylength,
+                      pk2->pubkey_algo,
+                      (ulong) keyid2[0], (ulong) keyid2[1],
+                      colon_datestr_from_pk (pk2),
+                      colon_strtime (pk2->expiredate));
+         print_capabilities (ctrl, pk2, NULL);
           es_putc (':', es_stdout);    /* End of field 13. */
           es_putc (':', es_stdout);    /* End of field 14. */
-          if (secret)
+          if (secret || has_secret)
             {
               if (stubkey)
                 es_putc ('#', es_stdout);
               else if (serialno)
                 es_fputs (serialno, es_stdout);
+              else if (has_secret)
+                es_putc ('+', es_stdout);
             }
           es_putc (':', es_stdout);    /* End of field 15. */
           es_putc (':', es_stdout);    /* End of field 16. */
-          if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
-              || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
+          if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
+              || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
             {
-              char *curve = openpgp_oid_to_str (pk->pkey[0]);
-              const char *name = openpgp_oid_to_curve (curve);
-              if (!*name || *name == '?')
-                name = curve;
-              es_fputs (name, es_stdout);
               xfree (curve);
+              curve = openpgp_oid_to_str (pk2->pkey[0]);
+              curvename = openpgp_oid_to_curve (curve, 0);
+              if (!curvename)
+                curvename = curve;
+              es_fputs (curvename, es_stdout);
             }
           es_putc (':', es_stdout);    /* End of field 17. */
+          print_compliance_flags (pk2, keylength, curvename);
+          es_putc (':', es_stdout);    /* End of field 18. */
          es_putc ('\n', es_stdout);
-         if (fpr > 1)
-           print_fingerprint (pk2, 0);
-         if (opt.with_key_data || opt.with_keygrip)
-            {
-              if (hexgrip)
-                es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
-              if (opt.with_key_data)
-                print_key_data (pk2);
-            }
+          print_fingerprint (ctrl, NULL, pk2, 0);
+          if (hexgrip)
+            es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
+          if (opt.with_key_data)
+            print_key_data (pk2);
        }
       else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE)
        {
@@ -1368,6 +1551,8 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
          char *sigstr;
          size_t fplen;
          byte fparray[MAX_FINGERPRINT_LEN];
+          char *siguid;
+          size_t siguidlen;
 
          if (sig->sig_class == 0x20 || sig->sig_class == 0x28
              || sig->sig_class == 0x30)
@@ -1389,11 +1574,11 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
            {
              PKT_public_key *signer_pk = NULL;
 
-             fflush (stdout);
+             es_fflush (es_stdout);
              if (opt.no_sig_cache)
                signer_pk = xmalloc_clear (sizeof (PKT_public_key));
 
-             rc = check_key_signature2 (keyblock, node, NULL, signer_pk,
+             rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk,
                                         NULL, NULL, NULL);
              switch (gpg_err_code (rc))
                {
@@ -1427,6 +1612,16 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
              rc = 0;
              sigrc = ' ';
            }
+
+         if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
+            siguid = get_user_id (ctrl, sig->keyid, &siguidlen);
+          else
+            {
+              siguid = NULL;
+              siguidlen = 0;
+            }
+
+
          es_fputs (sigstr, es_stdout);
          es_putc (':', es_stdout);
          if (sigrc != ' ')
@@ -1446,18 +1641,12 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
          es_fprintf (es_stdout, ":");
 
          if (sigrc == '%')
-           es_fprintf (es_stdout, "[%s] ", g10_errstr (rc));
-         else if (sigrc == '?')
-           ;
-         else if (!opt.fast_list_mode)
-           {
-             size_t n;
-             p = get_user_id (sig->keyid, &n);
-             es_write_sanitized (es_stdout, p, n, ":", NULL);
-             xfree (p);
-           }
+           es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc));
+         else if (siguid)
+            es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL);
+
          es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
-                 sig->flags.exportable ? 'x' : 'l');
+                      sig->flags.exportable ? 'x' : 'l');
 
          if (opt.no_sig_cache && opt.check_sigs && fprokay)
            {
@@ -1471,10 +1660,12 @@ list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
            print_subpackets_colon (sig);
 
          /* fixme: check or list other sigs here */
+          xfree (siguid);
        }
     }
 
-  xfree (hexgrip);
+  xfree (curve);
+  xfree (hexgrip_buffer);
   xfree (serialno);
 }
 
@@ -1493,7 +1684,7 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID &&
          ((attr && node->pkt->pkt.user_id->attrib_data) ||
           (!attr && !node->pkt->pkt.user_id->attrib_data)) &&
-         node->pkt->pkt.user_id->is_primary)
+         node->pkt->pkt.user_id->flags.primary)
        {
          primary = primary2 = node;
          for (node = node->next; node; primary2 = node, node = node->next)
@@ -1516,9 +1707,9 @@ do_reorder_keyblock (KBNODE keyblock, int attr)
       if (node->pkt->pkttype == PKT_USER_ID)
        break;
     }
-  assert (node);
-  assert (last);        /* The user ID is never the first packet.  */
-  assert (primary0);    /* Ditto (this is the node before primary).  */
+  log_assert (node);
+  log_assert (last);    /* The user ID is never the first packet.  */
+  log_assert (primary0); /* Ditto (this is the node before primary).  */
   if (node == primary)
     return; /* Already the first one.  */
 
@@ -1534,34 +1725,97 @@ reorder_keyblock (KBNODE keyblock)
   do_reorder_keyblock (keyblock, 0);
 }
 
-void
-list_keyblock (KBNODE keyblock, int secret, int fpr, void *opaque)
+static void
+list_keyblock (ctrl_t ctrl,
+               KBNODE keyblock, int secret, int has_secret, int fpr,
+               struct keylist_context *listctx)
 {
   reorder_keyblock (keyblock);
+
   if (opt.with_colons)
-    list_keyblock_colon (keyblock, secret, fpr);
+    list_keyblock_colon (ctrl, keyblock, secret, has_secret);
   else
-    list_keyblock_print (keyblock, secret, fpr, opaque);
+    list_keyblock_print (ctrl, keyblock, secret, fpr, listctx);
+
+  if (secret)
+    es_fflush (es_stdout);
 }
 
+
+/* Public function used by keygen to list a keyblock.  If NO_VALIDITY
+ * is set the validity of a key is never shown.  */
+void
+list_keyblock_direct (ctrl_t ctrl,
+                      kbnode_t keyblock, int secret, int has_secret, int fpr,
+                      int no_validity)
+{
+  struct keylist_context listctx;
+
+  memset (&listctx, 0, sizeof (listctx));
+  listctx.no_validity = !!no_validity;
+  list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx);
+  keylist_context_release (&listctx);
+}
+
+
+/* Print an hex digit in ICAO spelling.  */
+static void
+print_icao_hexdigit (estream_t fp, int c)
+{
+  static const char *list[16] = {
+    "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
+    "Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot"
+  };
+
+  tty_fprintf (fp, "%s", list[c&15]);
+}
+
+
 /*
  * Function to print the finperprint.
  * mode 0: as used in key listings, opt.with_colons is honored
  *      1: print using log_info ()
  *      2: direct use of tty
  *      3: direct use of tty but only primary key.
+ *      4: direct use of tty but only subkey.
+ *     10: Same as 0 but with_colons etc is ignored.
+ *     20: Same as 0 but using a compact format.
  *
  * Modes 1 and 2 will try and print both subkey and primary key
- * fingerprints.  A MODE with bit 7 set is used internally.
+ * fingerprints.  A MODE with bit 7 set is used internally.  If
+ * OVERRIDE_FP is not NULL that stream will be used in  0 instead
+ * of es_stdout or instead of the TTY in modes 2 and 3.
  */
 void
-print_fingerprint (PKT_public_key *pk, int mode)
+print_fingerprint (ctrl_t ctrl, estream_t override_fp,
+                   PKT_public_key *pk, int mode)
 {
-  byte array[MAX_FINGERPRINT_LEN], *p;
-  size_t i, n;
+  char hexfpr[2*MAX_FINGERPRINT_LEN+1];
+  char *p;
+  size_t i;
   estream_t fp;
   const char *text;
   int primary = 0;
+  int with_colons = opt.with_colons;
+  int with_icao   = opt.with_icao_spelling;
+  int compact = 0;
+
+  if (mode == 10)
+    {
+      mode = 0;
+      with_colons = 0;
+      with_icao = 0;
+    }
+  else if (mode == 20)
+    {
+      mode = 0;
+      with_colons = 0;
+      compact = 1;
+    }
+
+  if (!opt.fingerprint && !opt.with_fingerprint
+      && opt.with_subkey_fingerprint)
+    compact = 1;
 
   if (pk->main_keyid[0] == pk->keyid[0]
       && pk->main_keyid[1] == pk->keyid[1])
@@ -1579,8 +1833,8 @@ print_fingerprint (PKT_public_key *pk, int mode)
   if (!primary && (mode == 1 || mode == 2))
     {
       PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk));
-      get_pubkey (primary_pk, pk->main_keyid);
-      print_fingerprint (primary_pk, mode | 0x80);
+      get_pubkey (ctrl, primary_pk, pk->main_keyid);
+      print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80));
       free_public_key (primary_pk);
     }
 
@@ -1594,9 +1848,9 @@ print_fingerprint (PKT_public_key *pk, int mode)
     }
   else if (mode == 2)
     {
-      fp = NULL; /* Use tty.  */
+      fp = override_fp; /* Use tty or given stream.  */
       if (primary)
-       /* TRANSLATORS: this should fit into 24 bytes to that the
+       /* TRANSLATORS: this should fit into 24 bytes so that the
         * fingerprint data is properly aligned with the user ID */
        text = _(" Primary key fingerprint:");
       else
@@ -1604,39 +1858,63 @@ print_fingerprint (PKT_public_key *pk, int mode)
     }
   else if (mode == 3)
     {
-      fp = NULL; /* Use tty.  */
+      fp = override_fp; /* Use tty or given stream.  */
       text = _("      Key fingerprint =");
     }
+  else if (mode == 4)
+    {
+      fp = override_fp; /* Use tty or given stream.  */
+      text = _("      Subkey fingerprint:");
+    }
   else
     {
-      fp = es_stdout;
-      text = _("      Key fingerprint =");
+      fp = override_fp? override_fp : es_stdout;
+      if (opt.keyid_format == KF_NONE)
+        {
+          text = "     ";  /* To indent ICAO spelling.  */
+          compact = 1;
+        }
+      else
+        text = _("      Key fingerprint =");
     }
 
-  fingerprint_from_pk (pk, array, &n);
-  p = array;
-  if (opt.with_colons && !mode)
+  hexfingerprint (pk, hexfpr, sizeof hexfpr);
+  if (with_colons && !mode)
+    {
+      es_fprintf (fp, "fpr:::::::::%s:", hexfpr);
+    }
+  else if (compact && !opt.fingerprint && !opt.with_fingerprint)
     {
-      es_fprintf (fp, "fpr:::::::::");
-      for (i = 0; i < n; i++, p++)
-       es_fprintf (fp, "%02X", *p);
-      es_putc (':', fp);
+      tty_fprintf (fp, "%*s%s", 6, "", hexfpr);
     }
   else
     {
-      tty_fprintf (fp, "%s", text);
-      if (n == 20)
-       {
-         for (i = 0; i < n; i++, i++, p += 2)
-            tty_fprintf (fp, "%s %02X%02X", i==10? " ":"", *p, p[1]);
-       }
+      char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1];
+      format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr);
+      if (compact)
+        tty_fprintf (fp, "%*s%s", 6, "", fmtfpr);
       else
-       {
-         for (i = 0; i < n; i++, p++)
-            tty_fprintf (fp, "%s %02X", (i && !(i % 8))? " ":"", *p);
-       }
+        tty_fprintf (fp, "%s %s", text, fmtfpr);
     }
   tty_fprintf (fp, "\n");
+  if (!with_colons && with_icao)
+    {
+      ;
+      tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, "");
+      for (i = 0, p = hexfpr; *p; i++, p++)
+        {
+          if (!i)
+            ;
+          else if (!(i%8))
+            tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, "");
+          else if (!(i%4))
+            tty_fprintf (fp, "  ");
+          else
+            tty_fprintf (fp, " ");
+          print_icao_hexdigit (fp, xtoi_1 (p));
+        }
+      tty_fprintf (fp, "\"\n");
+    }
 }
 
 /* Print the serial number of an OpenPGP card if available.  */
@@ -1663,6 +1941,81 @@ print_card_serialno (const char *serialno)
 }
 
 
+/* Print a public or secret (sub)key line.  Example:
+ *
+ * pub   dsa2048 2007-12-31 [SC] [expires: 2018-12-31]
+ *       80615870F5BAD690333686D0F2AD85AC1E42B367
+ *
+ * pub   rsa2048 2017-12-31 [SC] [expires: 2028-12-31]
+ *       80615870F5BAD690333686D0F2AD85AC1E42B3671122334455
+ *
+ * Some global options may result in a different output format.  If
+ * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and
+ * depending on the value a flag character is shown:
+ *
+ *    1 := ' ' Regular secret key
+ *    2 := '#' Stub secret key
+ *    3 := '>' Secret key is on a token.
+ */
+void
+print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret)
+{
+  char pkstrbuf[PUBKEY_STRING_SIZE];
+
+  tty_fprintf (fp, "%s%c  %s",
+               pk->flags.primary? (secret? "sec":"pub")
+               /**/             : (secret? "ssb":"sub"),
+               secret == 2? '#' : secret == 3? '>' : ' ',
+               pubkey_string (pk, pkstrbuf, sizeof pkstrbuf));
+  if (opt.keyid_format != KF_NONE)
+    tty_fprintf (fp, "/%s", keystr_from_pk (pk));
+  tty_fprintf (fp, " %s", datestr_from_pk (pk));
+
+  if ((opt.list_options & LIST_SHOW_USAGE))
+    {
+      tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0));
+    }
+  if (pk->flags.revoked)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->has_expired)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+  else if (pk->expiredate)
+    {
+      tty_fprintf (fp, " [");
+      tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
+      tty_fprintf (fp, "]");
+    }
+
+#if 0
+  /* I need to think about this some more.  It's easy enough to
+     include, but it looks sort of confusing in the listing... */
+  if (opt.list_options & LIST_SHOW_VALIDITY)
+    {
+      int validity = get_validity (ctrl, pk, NULL, NULL, 0);
+      tty_fprintf (fp, " [%s]", trust_value_to_string (validity));
+    }
+#endif
+
+  if (pk->pubkey_algo >= 100)
+    tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo);
+
+  tty_fprintf (fp, "\n");
+
+  /* if the user hasn't explicitly asked for human-readable
+     fingerprints, show compact fpr of primary key: */
+  if (pk->flags.primary &&
+      !opt.fingerprint && !opt.with_fingerprint)
+    print_fingerprint (ctrl, fp, pk, 20);
+}
+
 
 void
 set_attrib_fd (int fd)
@@ -1679,6 +2032,9 @@ set_attrib_fd (int fd)
   if (fd == -1)
     return;
 
+  if (! gnupg_fd_valid (fd))
+    log_fatal ("attribute-fd is invalid: %s\n", strerror (errno));
+
 #ifdef HAVE_DOSISH_SYSTEM
   setmode (fd, O_BINARY);
 #endif