Adjust for changed macro names in libgpg-error master.
[gnupg.git] / g10 / keylist.c
index 51dc409..bcbad45 100644 (file)
@@ -16,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 "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 "mbox-util.h"
-#include "zb32.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 (ctrl_t, int, int);
@@ -92,7 +94,7 @@ public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
       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:");
@@ -164,7 +166,7 @@ secret_key_list (ctrl_t ctrl, strlist_t list)
 }
 
 char *
-format_seckey_info (PKT_public_key *pk)
+format_seckey_info (ctrl_t ctrl, PKT_public_key *pk)
 {
   u32 keyid[2];
   char *p;
@@ -172,7 +174,7 @@ format_seckey_info (PKT_public_key *pk)
   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),
@@ -184,18 +186,18 @@ format_seckey_info (PKT_public_key *pk)
 }
 
 void
-print_seckey_info (PKT_public_key *pk)
+print_seckey_info (ctrl_t ctrl, PKT_public_key *pk)
 {
-  char *p = format_seckey_info (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;
@@ -208,7 +210,7 @@ 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");
@@ -304,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.
@@ -314,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,
@@ -365,43 +366,43 @@ 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;
+  estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout;
   notation_t nd, notations;
 
   if (which == 0)
@@ -418,34 +419,32 @@ 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));
+             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)
+              if (mode > 0)
                 log_printf ("\n");
               else
-                es_putc ('\n', fp);
+                tty_fprintf (fp, "\n");
            }
        }
 
-      if (mode)
+      if (mode > 0)
        {
          write_status_buffer (STATUS_NOTATION_NAME,
                               nd->name, strlen (nd->name), 0);
@@ -468,6 +467,10 @@ print_signature_stats (struct keylist_context *s)
   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);
@@ -552,7 +555,7 @@ list_all (ctrl_t ctrl, int secret, int mark_secret)
                   lastresname = resname;
                 }
             }
-          merge_keys_and_selfsig (keyblock);
+          merge_keys_and_selfsig (ctrl, keyblock);
           list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint,
                          &listctx);
         }
@@ -595,7 +598,7 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret)
     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
@@ -603,11 +606,11 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_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", gpg_strerror (rc));
-      getkey_end (ctx);
+      getkey_end (ctrl, ctx);
       return;
     }
 
@@ -625,8 +628,8 @@ list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret)
                      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 (&listctx);
@@ -666,8 +669,8 @@ locate_one (ctrl_t ctrl, strlist_t names)
              list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx);
              release_kbnode (keyblock);
            }
-         while (ctx && !getkey_next (ctx, NULL, &keyblock));
-         getkey_end (ctx);
+         while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock));
+         getkey_end (ctrl, ctx);
          ctx = NULL;
        }
     }
@@ -694,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;
@@ -852,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);
        }
 
@@ -907,10 +940,10 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
     check_trustdb_stale (ctrl);
 
   /* Print the "pub" line and in KF_NONE mode the fingerprint.  */
-  print_key_line (es_stdout, pk, secret);
+  print_key_line (ctrl, es_stdout, pk, secret);
 
   if (fpr)
-    print_fingerprint (NULL, pk, 0);
+    print_fingerprint (ctrl, NULL, pk, 0);
 
   if (opt.with_keygrip && hexgrip)
     es_fprintf (es_stdout, "      Keygrip = %s\n", hexgrip);
@@ -921,6 +954,24 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
   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)
@@ -929,7 +980,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
           int indent;
           int kl = opt.keyid_format == KF_NONE? 10 : keystrlen ();
 
-         if ((uid->is_expired || uid->is_revoked)
+         if ((uid->flags.expired || uid->flags.revoked)
              && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
            {
              skip_sigs = 1;
@@ -941,7 +992,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
          if (attrib_fp && uid->attrib_data != NULL)
            dump_attribs (uid, pk);
 
-         if ((uid->is_revoked || uid->is_expired)
+         if ((uid->flags.revoked || uid->flags.expired)
              || ((opt.list_options & LIST_SHOW_UID_VALIDITY)
                   && !listctx->no_validity))
            {
@@ -986,6 +1037,22 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
               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 (ctrl, uid->attribs, uid->numattribs, pk, uid);
        }
@@ -1016,14 +1083,14 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
               if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL))
                 secret = serialno? 3 : 1;
               else
-                secret = '2';  /* Key not found.  */
+                secret = 2;  /* Key not found.  */
             }
 
           /* Print the "sub" line.  */
-          print_key_line (es_stdout, pk2, secret);
+          print_key_line (ctrl, es_stdout, pk2, secret);
          if (fpr > 1 || opt.with_subkey_fingerprint)
             {
-              print_fingerprint (NULL, pk2, 0);
+              print_fingerprint (ctrl, NULL, pk2, 0);
               if (serialno)
                 print_card_serialno (serialno);
             }
@@ -1031,6 +1098,8 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
             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)
@@ -1041,7 +1110,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
 
          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:
@@ -1113,7 +1182,7 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr,
          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);
            }
@@ -1170,6 +1239,37 @@ print_revokers (estream_t fp, PKT_public_key * pk)
 }
 
 
+/* 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.  */
@@ -1191,6 +1291,9 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
   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);
@@ -1228,25 +1331,27 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
     trustletter_print = 0;
   else
     {
-      trustletter = get_validity_info (ctrl, pk, NULL);
+      trustletter = get_validity_info (ctrl, keyblock, pk, NULL);
       if (trustletter == 'u')
         ulti_hack = 1;
       trustletter_print = trustletter;
     }
 
   if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
-    ownertrust_print = get_ownertrust_info (pk);
+    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 (ownertrust_print)
     es_putc (ownertrust_print, es_stdout);
@@ -1254,7 +1359,7 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
 
   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 || has_secret)
@@ -1272,19 +1377,27 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
       || 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, 0);
-      if (!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. */
-  es_putc (':', es_stdout);            /* End of field 18. */
+  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 (es_stdout, pk);
-  print_fingerprint (NULL, pk, 0);
+  print_fingerprint (ctrl, NULL, pk, 0);
   if (hexgrip)
     es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
   if (opt.with_key_data)
@@ -1300,16 +1413,16 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
          if (attrib_fp && uid->attrib_data != NULL)
            dump_attribs (uid, pk);
 
-         if (uid->is_revoked)
+         if (uid->flags.revoked)
            uid_validity = 'r';
-         else if (uid->is_expired)
+         else if (uid->flags.expired)
            uid_validity = 'e';
          else if (opt.no_expensive_trust_checks)
            uid_validity = 0;
          else if (ulti_hack)
             uid_validity = 'u';
           else
-            uid_validity = get_validity_info (ctrl, pk, uid);
+            uid_validity = get_validity_info (ctrl, keyblock, pk, uid);
 
           es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout);
           if (uid_validity)
@@ -1330,7 +1443,16 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
            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
@@ -1380,14 +1502,14 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
              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 || has_secret)
@@ -1405,16 +1527,18 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
               || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
               || pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
             {
-              char *curve = openpgp_oid_to_str (pk2->pkey[0]);
-              const char *name = openpgp_oid_to_curve (curve, 0);
-              if (!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);
-          print_fingerprint (NULL, pk2, 0);
+          print_fingerprint (ctrl, NULL, pk2, 0);
           if (hexgrip)
             es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
           if (opt.with_key_data)
@@ -1450,11 +1574,11 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
            {
              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))
                {
@@ -1490,7 +1614,7 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
            }
 
          if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode)
-            siguid = get_user_id (sig->keyid, &siguidlen);
+            siguid = get_user_id (ctrl, sig->keyid, &siguidlen);
           else
             {
               siguid = NULL;
@@ -1540,6 +1664,7 @@ list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock,
        }
     }
 
+  xfree (curve);
   xfree (hexgrip_buffer);
   xfree (serialno);
 }
@@ -1559,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)
@@ -1662,7 +1787,8 @@ print_icao_hexdigit (estream_t fp, int c)
  * of es_stdout or instead of the TTY in modes 2 and 3.
  */
 void
-print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
+print_fingerprint (ctrl_t ctrl, estream_t override_fp,
+                   PKT_public_key *pk, int mode)
 {
   char hexfpr[2*MAX_FINGERPRINT_LEN+1];
   char *p;
@@ -1707,8 +1833,8 @@ print_fingerprint (estream_t override_fp, 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 (override_fp, 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);
     }
 
@@ -1820,6 +1946,9 @@ print_card_serialno (const char *serialno)
  * 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:
@@ -1829,7 +1958,7 @@ print_card_serialno (const char *serialno)
  *    3 := '>' Secret key is on a token.
  */
 void
-print_key_line (estream_t fp, PKT_public_key *pk, int secret)
+print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret)
 {
   char pkstrbuf[PUBKEY_STRING_SIZE];
 
@@ -1884,7 +2013,7 @@ print_key_line (estream_t fp, PKT_public_key *pk, int secret)
      fingerprints, show compact fpr of primary key: */
   if (pk->flags.primary &&
       !opt.fingerprint && !opt.with_fingerprint)
-    print_fingerprint (fp, pk, 20);
+    print_fingerprint (ctrl, fp, pk, 20);
 }
 
 
@@ -1903,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