gpg: Avoid endless loop in a tofu error case.
[gnupg.git] / g10 / keyedit.c
index 804eff1..d05ea5d 100644 (file)
@@ -1,7 +1,7 @@
-/* keyedit.c - keyedit stuff
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- *               2008, 2009, 2010 Free Software Foundation, Inc.
- * Copyright (C) 2013, 2014 Werner Koch
+/* keyedit.c - Edit properties of a key
+ * Copyright (C) 1998-2010 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2016 Werner Koch
+ * Copyright (C) 2015, 2016 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -24,7 +24,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <ctype.h>
 #ifdef HAVE_LIBREADLINE
 # define GNUPG_LIBREADLINE_H_INCLUDED
 #include "i18n.h"
 #include "keyserver-internal.h"
 #include "call-agent.h"
+#include "host2net.h"
+#include "tofu.h"
 
 static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
                        int verbose);
-static void show_names (estream_t fp, KBNODE keyblock, PKT_public_key * pk,
+static void show_names (ctrl_t ctrl, estream_t fp,
+                        kbnode_t keyblock, PKT_public_key * pk,
                        unsigned int flag, int with_prefs);
-static void show_key_with_all_names (estream_t fp,
+static void show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                                      KBNODE keyblock, int only_marked,
                                     int with_revoker, int with_fpr,
                                     int with_subkeys, int with_prefs,
                                      int nowarn);
-static void show_key_and_fingerprint (KBNODE keyblock);
+static void show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys);
+static void show_key_and_grip (kbnode_t keyblock);
 static void subkey_expire_warning (kbnode_t keyblock);
-static int menu_adduid (KBNODE keyblock, int photo, const char *photo_name);
+static int menu_adduid (ctrl_t ctrl, kbnode_t keyblock,
+                        int photo, const char *photo_name, const char *uidstr);
 static void menu_deluid (KBNODE pub_keyblock);
 static int menu_delsig (KBNODE pub_keyblock);
 static int menu_clean (KBNODE keyblock, int self_only);
 static void menu_delkey (KBNODE pub_keyblock);
 static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive);
 static int menu_expire (KBNODE pub_keyblock);
+static int menu_changeusage (kbnode_t keyblock);
 static int menu_backsign (KBNODE pub_keyblock);
 static int menu_set_primary_uid (KBNODE pub_keyblock);
 static int menu_set_preferences (KBNODE pub_keyblock);
@@ -73,7 +78,7 @@ static int menu_set_keyserver_url (const char *url, KBNODE pub_keyblock);
 static int menu_set_notation (const char *string, KBNODE pub_keyblock);
 static int menu_select_uid (KBNODE keyblock, int idx);
 static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash);
-static int menu_select_key (KBNODE keyblock, int idx);
+static int menu_select_key (KBNODE keyblock, int idx, char *p);
 static int count_uids (KBNODE keyblock);
 static int count_uids_with_flag (KBNODE keyblock, unsigned flag);
 static int count_keys_with_flag (KBNODE keyblock, unsigned flag);
@@ -81,13 +86,13 @@ static int count_selected_uids (KBNODE keyblock);
 static int real_uids_left (KBNODE keyblock);
 static int count_selected_keys (KBNODE keyblock);
 static int menu_revsig (KBNODE keyblock);
-static int menu_revuid (KBNODE keyblock);
+static int menu_revuid (ctrl_t ctrl, kbnode_t keyblock);
 static int menu_revkey (KBNODE pub_keyblock);
 static int menu_revsubkey (KBNODE pub_keyblock);
 #ifndef NO_TRUST_MODELS
 static int enable_disable_key (KBNODE keyblock, int disable);
 #endif /*!NO_TRUST_MODELS*/
-static void menu_showphoto (KBNODE keyblock);
+static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock);
 
 static int update_trust = 0;
 
@@ -184,22 +189,23 @@ print_and_check_one_sig_colon (KBNODE keyblock, KBNODE node,
 
 
 /*
- * Print information about a signature, check it and return true
- * if the signature is okay. NODE must be a signature packet.
+ * Print information about a signature (rc is its status), check it
+ * and return true if the signature is okay.  NODE must be a signature
+ * packet.  With EXTENDED set all possible signature list options will
+ * always be printed.
  */
 static int
-print_and_check_one_sig (KBNODE keyblock, KBNODE node,
-                        int *inv_sigs, int *no_key, int *oth_err,
-                        int *is_selfsig, int print_without_key)
+print_one_sig (int rc, KBNODE keyblock, KBNODE node,
+               int *inv_sigs, int *no_key, int *oth_err,
+               int is_selfsig, int print_without_key, int extended)
 {
   PKT_signature *sig = node->pkt->pkt.signature;
-  int rc, sigrc;
+  int sigrc;
   int is_rev = sig->sig_class == 0x30;
 
   /* TODO: Make sure a cached sig record here still has the pk that
      issued it.  See also keylist.c:list_keyblock_print */
 
-  rc = check_key_signature (keyblock, node, is_selfsig);
   switch (gpg_err_code (rc))
     {
     case 0:
@@ -240,18 +246,21 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
                  sig->flags.expired ? 'X' : ' ',
                  (sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
                                                  0) ? '0' +
-                 sig->trust_depth : ' ', keystr (sig->keyid),
+                 sig->trust_depth : ' ',
+                  keystr (sig->keyid),
                  datestr_from_sig (sig));
-      if (opt.list_options & LIST_SHOW_SIG_EXPIRE)
+      if ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended )
        tty_printf (" %s", expirestr_from_sig (sig));
       tty_printf ("  ");
       if (sigrc == '%')
        tty_printf ("[%s] ", gpg_strerror (rc));
       else if (sigrc == '?')
        ;
-      else if (*is_selfsig)
+      else if (is_selfsig)
        {
          tty_printf (is_rev ? _("[revocation]") : _("[self-signature]"));
+          if (extended && sig->flags.chosen_selfsig)
+            tty_printf ("*");
        }
       else
        {
@@ -266,97 +275,650 @@ print_and_check_one_sig (KBNODE keyblock, KBNODE node,
        }
       tty_printf ("\n");
 
-      if (sig->flags.policy_url && (opt.list_options & LIST_SHOW_POLICY_URLS))
+      if (sig->flags.policy_url
+          && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended))
        show_policy_url (sig, 3, 0);
 
-      if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
+      if (sig->flags.notation
+          && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended))
        show_notation (sig, 3, 0,
                       ((opt.
                         list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) +
                       ((opt.
                         list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0));
 
-      if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
+      if (sig->flags.pref_ks
+          && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended))
        show_keyserver_url (sig, 3, 0);
+
+      if (extended)
+        {
+          PKT_public_key *pk = keyblock->pkt->pkt.public_key;
+          const unsigned char *s;
+
+          s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL);
+          if (s && *s)
+            tty_printf ("             [primary]\n");
+
+          s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
+          if (s && buf32_to_u32 (s))
+            tty_printf ("             [expires: %s]\n",
+                        isotimestamp (pk->timestamp + buf32_to_u32 (s)));
+        }
     }
 
   return (sigrc == '!');
 }
 
 
+static int
+print_and_check_one_sig (KBNODE keyblock, KBNODE node,
+                        int *inv_sigs, int *no_key, int *oth_err,
+                        int *is_selfsig, int print_without_key, int extended)
+{
+  int rc;
 
-/*
- * Check the keysigs and set the flags to indicate errors.
- * Returns true if error found.
- */
+  rc = check_key_signature (keyblock, node, is_selfsig);
+  return print_one_sig (rc,
+                        keyblock, node, inv_sigs, no_key, oth_err,
+                        *is_selfsig, print_without_key, extended);
+}
+
+
+
+/* Order two signatures.  The actual ordering isn't important.  Our
+   goal is to ensure that identical signatures occur together.  */
 static int
-check_all_keysigs (KBNODE keyblock, int only_selected)
+sig_comparison (const void *av, const void *bv)
 {
-  KBNODE kbctx;
-  KBNODE node;
-  int inv_sigs = 0;
-  int no_key = 0;
-  int oth_err = 0;
-  int has_selfsig = 0;
-  int mis_selfsig = 0;
-  int selected = !only_selected;
-  int anyuid = 0;
-
-  for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
+  const KBNODE an = *(const KBNODE *) av;
+  const KBNODE bn = *(const KBNODE *) bv;
+  const PKT_signature *a;
+  const PKT_signature *b;
+  int ndataa;
+  int ndatab;
+  int i;
+
+  log_assert (an->pkt->pkttype == PKT_SIGNATURE);
+  log_assert (bn->pkt->pkttype == PKT_SIGNATURE);
+
+  a = an->pkt->pkt.signature;
+  b = bn->pkt->pkt.signature;
+
+  if (a->digest_algo < b->digest_algo)
+    return -1;
+  if (a->digest_algo > b->digest_algo)
+    return 1;
+
+  ndataa = pubkey_get_nsig (a->pubkey_algo);
+  ndatab = pubkey_get_nsig (b->pubkey_algo);
+  if (ndataa != ndatab)
+    return (ndataa < ndatab)? -1 : 1;
+
+  for (i = 0; i < ndataa; i ++)
     {
-      if (node->pkt->pkttype == PKT_USER_ID)
-       {
-         PKT_user_id *uid = node->pkt->pkt.user_id;
+      int c = gcry_mpi_cmp (a->data[i], b->data[i]);
+      if (c != 0)
+        return c;
+    }
 
-         if (only_selected)
-           selected = (node->flag & NODFLG_SELUID);
-         if (selected)
-           {
-             tty_printf ("uid  ");
-             tty_print_utf8_string (uid->name, uid->len);
-             tty_printf ("\n");
-             if (anyuid && !has_selfsig)
-               mis_selfsig++;
-             has_selfsig = 0;
-             anyuid = 1;
-           }
-       }
-      else if (selected && node->pkt->pkttype == PKT_SIGNATURE
-              && ((node->pkt->pkt.signature->sig_class & ~3) == 0x10
-                  || node->pkt->pkt.signature->sig_class == 0x30))
-       {
-         int selfsig;
+  /* Okay, they are equal.  */
+  return 0;
+}
 
-         if (print_and_check_one_sig (keyblock, node, &inv_sigs,
-                                      &no_key, &oth_err, &selfsig, 0))
-           {
-             if (selfsig)
-               has_selfsig = 1;
-           }
-         /* Hmmm: should we update the trustdb here? */
-       }
+/* Perform a few sanity checks on a keyblock is okay and possibly
+   repair some damage.  Concretely:
+
+     - Detect duplicate signatures and remove them.
+
+     - Detect out of order signatures and relocate them (e.g., a sig
+       over user id X located under subkey Y).
+
+   Note: this function does not remove signatures that don't belong or
+   components that are not signed!  (Although it would be trivial to
+   do so.)
+
+   If ONLY_SELFSIGS is true, then this function only reorders self
+   signatures (it still checks all signatures for duplicates,
+   however).
+
+   Returns 1 if the keyblock was modified, 0 otherwise.  */
+static int
+check_all_keysigs (KBNODE kb, int only_selected, int only_selfsigs)
+{
+  gpg_error_t err;
+  PKT_public_key *pk;
+  KBNODE n, n_next, *n_prevp, n2;
+  char *pending_desc = NULL;
+  PKT_public_key *issuer;
+  KBNODE last_printed_component;
+  KBNODE current_component = NULL;
+  int dups = 0;
+  int missing_issuer = 0;
+  int reordered = 0;
+  int bad_signature = 0;
+  int missing_selfsig = 0;
+  int modified = 0;
+
+  log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
+  pk = kb->pkt->pkt.public_key;
+
+  /* First we look for duplicates.  */
+  {
+    int nsigs = 0;
+    KBNODE *sigs;
+    int i;
+    int last_i;
+
+    /* Count the sigs.  */
+    for (n = kb; n; n = n->next)
+      if (is_deleted_kbnode (n))
+        continue;
+      else if (n->pkt->pkttype == PKT_SIGNATURE)
+        nsigs ++;
+
+    /* Add them all to the SIGS array.  */
+    sigs = xmalloc_clear (sizeof (*sigs) * nsigs);
+
+    i = 0;
+    for (n = kb; n; n = n->next)
+      {
+        if (is_deleted_kbnode (n))
+          continue;
+
+        if (n->pkt->pkttype != PKT_SIGNATURE)
+          continue;
+
+        sigs[i] = n;
+        i ++;
+      }
+    log_assert (i == nsigs);
+
+    qsort (sigs, nsigs, sizeof (sigs[0]), sig_comparison);
+
+    last_i = 0;
+    for (i = 1; i < nsigs; i ++)
+      {
+        log_assert (sigs[last_i]);
+        log_assert (sigs[last_i]->pkt->pkttype == PKT_SIGNATURE);
+        log_assert (sigs[i]);
+        log_assert (sigs[i]->pkt->pkttype == PKT_SIGNATURE);
+
+        if (sig_comparison (&sigs[last_i], &sigs[i]) == 0)
+          /* They are the same.  Kill the latter.  */
+          {
+            if (DBG_PACKET)
+              {
+                PKT_signature *sig = sigs[i]->pkt->pkt.signature;
+
+                log_debug ("Signature appears multiple times, "
+                           "deleting duplicate:\n");
+                log_debug ("  sig: class 0x%x, issuer: %s,"
+                           " timestamp: %s (%lld), digest: %02x %02x\n",
+                           sig->sig_class, keystr (sig->keyid),
+                           isotimestamp (sig->timestamp),
+                           (long long) sig->timestamp,
+                           sig->digest_start[0], sig->digest_start[1]);
+              }
+
+            /* Remove sigs[i] from the keyblock.  */
+            {
+              KBNODE z, *prevp;
+              int to_kill = last_i;
+              last_i = i;
+
+              for (prevp = &kb, z = kb; z; prevp = &z->next, z = z->next)
+                if (z == sigs[to_kill])
+                  break;
+
+              *prevp = sigs[to_kill]->next;
+
+              sigs[to_kill]->next = NULL;
+              release_kbnode (sigs[to_kill]);
+              sigs[to_kill] = NULL;
+
+              dups ++;
+              modified = 1;
+            }
+          }
+        else
+          last_i = i;
+      }
+
+    xfree (sigs);
+  }
+
+  /* Make sure the sigs occur after the component (public key, subkey,
+     user id) that they sign.  */
+  issuer = NULL;
+  last_printed_component = NULL;
+  for (n_prevp = &kb, n = kb;
+       n;
+       /* If we moved n, then n_prevp is need valid.  */
+       n_prevp = (n->next == n_next ? &n->next : n_prevp), n = n_next)
+    {
+      PACKET *p;
+      int processed_current_component;
+      PKT_signature *sig;
+      int rc;
+      int dump_sig_params = 0;
+
+      n_next = n->next;
+
+      if (is_deleted_kbnode (n))
+        continue;
+
+      p = n->pkt;
+
+      if (issuer && issuer != pk)
+        {
+          free_public_key (issuer);
+          issuer = NULL;
+        }
+
+      xfree (pending_desc);
+      pending_desc = NULL;
+
+      switch (p->pkttype)
+        {
+        case PKT_PUBLIC_KEY:
+          log_assert (p->pkt.public_key == pk);
+          if (only_selected && ! (n->flag & NODFLG_SELKEY))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("public key %s: timestamp: %s (%lld)\n",
+                       pk_keyid_str (pk),
+                       isotimestamp (pk->timestamp),
+                       (long long) pk->timestamp);
+          current_component = n;
+          break;
+        case PKT_PUBLIC_SUBKEY:
+          if (only_selected && ! (n->flag & NODFLG_SELKEY))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("subkey %s: timestamp: %s (%lld)\n",
+                       pk_keyid_str (p->pkt.public_key),
+                       isotimestamp (p->pkt.public_key->timestamp),
+                       (long long) p->pkt.public_key->timestamp);
+          current_component = n;
+          break;
+        case PKT_USER_ID:
+          if (only_selected && ! (n->flag & NODFLG_SELUID))
+            {
+              current_component = NULL;
+              break;
+            }
+
+          if (DBG_PACKET)
+            log_debug ("user id: %s\n",
+                       p->pkt.user_id->attrib_data
+                       ? "[ photo id ]"
+                       : p->pkt.user_id->name);
+          current_component = n;
+          break;
+        case PKT_SIGNATURE:
+          if (! current_component)
+            /* The current component is not selected, don't check the
+               sigs under it.  */
+            break;
+
+          sig = n->pkt->pkt.signature;
+
+          pending_desc = xasprintf ("  sig: class: 0x%x, issuer: %s,"
+                                    " timestamp: %s (%lld), digest: %02x %02x",
+                                    sig->sig_class,
+                                    keystr (sig->keyid),
+                                    isotimestamp (sig->timestamp),
+                                    (long long) sig->timestamp,
+                                    sig->digest_start[0], sig->digest_start[1]);
+
+
+          if (keyid_cmp (pk_keyid (pk), sig->keyid) == 0)
+            issuer = pk;
+          else
+            /* Issuer is a different key.  */
+            {
+              if (only_selfsigs)
+                continue;
+
+              issuer = xmalloc (sizeof (*issuer));
+              err = get_pubkey (issuer, sig->keyid);
+              if (err)
+                {
+                  xfree (issuer);
+                  issuer = NULL;
+                  if (DBG_PACKET)
+                    {
+                      if (pending_desc)
+                        log_debug ("%s", pending_desc);
+                      log_debug ("    Can't check signature allegedly"
+                                 " issued by %s: %s\n",
+                                 keystr (sig->keyid), gpg_strerror (err));
+                    }
+                  missing_issuer ++;
+                  break;
+                }
+            }
+
+          if ((err = openpgp_pk_test_algo (sig->pubkey_algo)))
+            {
+              if (DBG_PACKET && pending_desc)
+                log_debug ("%s", pending_desc);
+              tty_printf (_("can't check signature with unsupported"
+                            " public-key algorithm (%d): %s.\n"),
+                          sig->pubkey_algo, gpg_strerror (err));
+              break;
+            }
+          if ((err = openpgp_md_test_algo (sig->digest_algo)))
+            {
+              if (DBG_PACKET && pending_desc)
+                log_debug ("%s", pending_desc);
+              tty_printf (_("can't check signature with unsupported"
+                            " message-digest algorithm %d: %s.\n"),
+                          sig->digest_algo, gpg_strerror (err));
+              break;
+            }
+
+          /* We iterate over the keyblock.  Most likely, the matching
+             component is the current component so always try that
+             first.  */
+          processed_current_component = 0;
+          for (n2 = current_component;
+               n2;
+               n2 = (processed_current_component ? n2->next : kb),
+                 processed_current_component = 1)
+            if (is_deleted_kbnode (n2))
+              continue;
+            else if (processed_current_component && n2 == current_component)
+              /* Don't process it twice.  */
+              continue;
+            else
+              {
+                err = check_signature_over_key_or_uid (issuer, sig, kb, n2->pkt,
+                                                       NULL, NULL);
+                if (! err)
+                  break;
+              }
+
+          /* n/sig is a signature and n2 is the component (public key,
+             subkey or user id) that it signs, if any.
+             current_component is that component that it appears to
+             apply to (according to the ordering).  */
+
+          if (current_component == n2)
+            {
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Good signature over last key or uid!\n");
+                }
+
+              rc = 0;
+            }
+          else if (n2)
+            {
+              log_assert (n2->pkt->pkttype == PKT_USER_ID
+                          || n2->pkt->pkttype == PKT_PUBLIC_KEY
+                          || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY);
+
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Good signature out of order!"
+                             "  (Over %s (%d) '%s')\n",
+                             n2->pkt->pkttype == PKT_USER_ID
+                             ? "user id"
+                             : n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                             ? "subkey"
+                             : "primary key",
+                             n2->pkt->pkttype,
+                             n2->pkt->pkttype == PKT_USER_ID
+                             ? n2->pkt->pkt.user_id->name
+                             : pk_keyid_str (n2->pkt->pkt.public_key));
+                }
+
+              /* Reorder the packets: move the signature n to be just
+                 after n2.  */
+
+              /* Unlink the signature.  */
+              log_assert (n_prevp);
+              *n_prevp = n->next;
+
+              /* Insert the sig immediately after the component.  */
+              n->next = n2->next;
+              n2->next = n;
+
+              reordered ++;
+              modified = 1;
+
+              rc = 0;
+            }
+          else
+            {
+              if (DBG_PACKET)
+                {
+                  log_debug ("%s", pending_desc);
+                  log_debug ("    Bad signature.\n");
+                }
+
+              if (DBG_PACKET)
+                dump_sig_params = 1;
+
+              bad_signature ++;
+
+              rc = GPG_ERR_BAD_SIGNATURE;
+            }
+
+          /* We don't cache the result here, because we haven't
+             completely checked that the signature is legitimate.  For
+             instance, if we have a revocation certificate on Alice's
+             key signed by Bob, the signature may be good, but we
+             haven't checked that Bob is a designated revoker.  */
+          /* cache_sig_result (sig, rc); */
+
+          {
+            int has_selfsig = 0;
+            if (! rc && issuer == pk)
+              {
+                if (n2->pkt->pkttype == PKT_PUBLIC_KEY
+                    && (/* Direct key signature.  */
+                        sig->sig_class == 0x1f
+                        /* Key revocation signature.  */
+                        || sig->sig_class == 0x20))
+                  has_selfsig = 1;
+                if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                    && (/* Subkey binding sig.  */
+                        sig->sig_class == 0x18
+                        /* Subkey revocation sig.  */
+                        || sig->sig_class == 0x28))
+                  has_selfsig = 1;
+                if (n2->pkt->pkttype == PKT_USER_ID
+                    && (/* Certification sigs.  */
+                        sig->sig_class == 0x10
+                        || sig->sig_class == 0x11
+                        || sig->sig_class == 0x12
+                        || sig->sig_class == 0x13
+                        /* Certification revocation sig.  */
+                        || sig->sig_class == 0x30))
+                  has_selfsig = 1;
+              }
+
+            if ((n2 && n2 != last_printed_component)
+                || (! n2 && last_printed_component != current_component))
+              {
+                int is_reordered = n2 && n2 != current_component;
+                if (n2)
+                  last_printed_component = n2;
+                else
+                  last_printed_component = current_component;
+
+                if (!modified)
+                  ;
+                else if (last_printed_component->pkt->pkttype == PKT_USER_ID)
+                  {
+                    tty_printf ("uid  ");
+                    tty_print_utf8_string (last_printed_component
+                                           ->pkt->pkt.user_id->name,
+                                           last_printed_component
+                                           ->pkt->pkt.user_id->len);
+                  }
+                else if (last_printed_component->pkt->pkttype
+                         == PKT_PUBLIC_KEY)
+                  tty_printf ("pub  %s",
+                              pk_keyid_str (last_printed_component
+                                            ->pkt->pkt.public_key));
+                else
+                  tty_printf ("sub  %s",
+                              pk_keyid_str (last_printed_component
+                                            ->pkt->pkt.public_key));
+
+                if (modified)
+                  {
+                    if (is_reordered)
+                      tty_printf (_(" (reordered signatures follow)"));
+                    tty_printf ("\n");
+                  }
+              }
+
+            if (modified)
+              print_one_sig (rc, kb, n, NULL, NULL, NULL, has_selfsig,
+                             0, only_selfsigs);
+          }
+
+          if (dump_sig_params)
+            {
+              int i;
+
+              for (i = 0; i < pubkey_get_nsig (sig->pubkey_algo); i ++)
+                {
+                  char buffer[1024];
+                  size_t len;
+                  char *printable;
+                  gcry_mpi_print (GCRYMPI_FMT_USG,
+                                  buffer, sizeof (buffer), &len,
+                                  sig->data[i]);
+                  printable = bin2hex (buffer, len, NULL);
+                  log_info ("        %d: %s\n", i, printable);
+                  xfree (printable);
+                }
+            }
+          break;
+        default:
+          if (DBG_PACKET)
+            log_debug ("unhandled packet: %d\n", p->pkttype);
+          break;
+        }
     }
-  if (!has_selfsig)
-    mis_selfsig++;
-  if (inv_sigs == 1)
-    tty_printf (_("1 bad signature\n"));
-  else if (inv_sigs)
-    tty_printf (_("%d bad signatures\n"), inv_sigs);
-  if (no_key == 1)
-    tty_printf (_("1 signature not checked due to a missing key\n"));
-  else if (no_key)
-    tty_printf (_("%d signatures not checked due to missing keys\n"), no_key);
-  if (oth_err == 1)
-    tty_printf (_("1 signature not checked due to an error\n"));
-  else if (oth_err)
-    tty_printf (_("%d signatures not checked due to errors\n"), oth_err);
-  if (mis_selfsig == 1)
-    tty_printf (_("1 user ID without valid self-signature detected\n"));
-  else if (mis_selfsig)
-    tty_printf (_("%d user IDs without valid self-signatures detected\n"),
-               mis_selfsig);
-
-  return inv_sigs || no_key || oth_err || mis_selfsig;
+
+  xfree (pending_desc);
+  pending_desc = NULL;
+
+  if (issuer != pk)
+    free_public_key (issuer);
+  issuer = NULL;
+
+  /* Identify keys / uids that don't have a self-sig.  */
+  {
+    int has_selfsig = 0;
+    PACKET *p;
+    PKT_signature *sig;
+
+    current_component = NULL;
+    for (n = kb; n; n = n->next)
+      {
+        if (is_deleted_kbnode (n))
+          continue;
+
+        p = n->pkt;
+
+        switch (p->pkttype)
+          {
+          case PKT_PUBLIC_KEY:
+          case PKT_PUBLIC_SUBKEY:
+          case PKT_USER_ID:
+            if (current_component && ! has_selfsig)
+              missing_selfsig ++;
+            current_component = n;
+            has_selfsig = 0;
+            break;
+
+          case PKT_SIGNATURE:
+            if (! current_component || has_selfsig)
+              break;
+
+            sig = n->pkt->pkt.signature;
+
+            if (! (sig->flags.checked && sig->flags.valid))
+              break;
+
+            if (keyid_cmp (pk_keyid (pk), sig->keyid) != 0)
+              /* Different issuer, couldn't be a self-sig.  */
+              break;
+
+            if (current_component->pkt->pkttype == PKT_PUBLIC_KEY
+                && (/* Direct key signature.  */
+                    sig->sig_class == 0x1f
+                    /* Key revocation signature.  */
+                    || sig->sig_class == 0x20))
+              has_selfsig = 1;
+            if (current_component->pkt->pkttype == PKT_PUBLIC_SUBKEY
+                && (/* Subkey binding sig.  */
+                    sig->sig_class == 0x18
+                    /* Subkey revocation sig.  */
+                    || sig->sig_class == 0x28))
+              has_selfsig = 1;
+            if (current_component->pkt->pkttype == PKT_USER_ID
+                && (/* Certification sigs.  */
+                    sig->sig_class == 0x10
+                    || sig->sig_class == 0x11
+                    || sig->sig_class == 0x12
+                    || sig->sig_class == 0x13
+                    /* Certification revocation sig.  */
+                    || sig->sig_class == 0x30))
+              has_selfsig = 1;
+
+            break;
+
+          default:
+            if (current_component && ! has_selfsig)
+              missing_selfsig ++;
+            current_component = NULL;
+          }
+      }
+  }
+
+  if (dups || missing_issuer || bad_signature || reordered)
+    tty_printf (_("key %s:\n"), pk_keyid_str (pk));
+
+  if (dups)
+    tty_printf (ngettext ("%d duplicate signature removed\n",
+                          "%d duplicate signatures removed\n", dups), dups);
+  if (missing_issuer)
+    tty_printf (ngettext ("%d signature not checked due to a missing key\n",
+                          "%d signatures not checked due to missing keys\n",
+                          missing_issuer), missing_issuer);
+  if (bad_signature)
+    tty_printf (ngettext ("%d bad signature\n",
+                          "%d bad signatures\n",
+                          bad_signature), bad_signature);
+  if (reordered)
+    tty_printf (ngettext ("%d signature reordered\n",
+                          "%d signatures reordered\n",
+                          reordered), reordered);
+
+  if (only_selfsigs && (bad_signature || reordered))
+    tty_printf (_("Warning: errors found and only checked self-signatures,"
+                  " run '%s' to check all signatures.\n"), "check");
+
+  return modified;
 }
 
 
@@ -437,7 +999,8 @@ trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp)
   tty_printf ("\n");
 
   tty_printf (_("Please enter the depth of this trust signature.\n"
-               "A depth greater than 1 allows the key you are signing to make\n"
+               "A depth greater than 1 allows the key you are"
+                " signing to make\n"
                "trust signatures on your behalf.\n"));
   tty_printf ("\n");
 
@@ -508,7 +1071,7 @@ trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp)
  * function won't ask the user and use sensible defaults.
  */
 static int
-sign_uids (estream_t fp,
+sign_uids (ctrl_t ctrl, estream_t fp,
            kbnode_t keyblock, strlist_t locusr, int *ret_modified,
           int local, int nonrevocable, int trust, int interactive,
            int quick)
@@ -528,7 +1091,7 @@ sign_uids (estream_t fp,
    * why to sign keys using a subkey.  Implementation of USAGE_CERT
    * is just a hack in getkey.c and does not mean that a subkey
    * marked as certification capable will be used. */
-  rc = build_sk_list (locusr, &sk_list, PUBKEY_USAGE_CERT);
+  rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT);
   if (rc)
     goto leave;
 
@@ -577,7 +1140,16 @@ sign_uids (estream_t fp,
                   user = utf8_to_native (uidnode->pkt->pkt.user_id->name,
                                          uidnode->pkt->pkt.user_id->len, 0);
 
-                 if (uidnode->pkt->pkt.user_id->is_revoked)
+                  if (opt.only_sign_text_ids
+                      && uidnode->pkt->pkt.user_id->attribs)
+                    {
+                      tty_fprintf (fp, _("Skipping user ID \"%s\","
+                                         " which is not a text ID.\n"),
+                                   user);
+                      uidnode->flag &= ~NODFLG_MARK_A;
+                      uidnode = NULL;
+                    }
+                 else if (uidnode->pkt->pkt.user_id->is_revoked)
                    {
                      tty_fprintf (fp, _("User ID \"%s\" is revoked."), user);
 
@@ -804,11 +1376,19 @@ sign_uids (estream_t fp,
 
       /* Ask whether we really should sign these user id(s). */
       tty_fprintf (fp, "\n");
-      show_key_with_all_names (fp, keyblock, 1, 0, 1, 0, 0, 0);
+      show_key_with_all_names (ctrl, fp, keyblock, 1, 0, 1, 0, 0, 0);
       tty_fprintf (fp, "\n");
 
       if (primary_pk->expiredate && !selfsig)
        {
+          /* Static analyzer note: A claim that PRIMARY_PK might be
+             NULL is not correct because it set from the public key
+             packet which is always the first packet in a keyblock and
+             parsed in the above loop over the keyblock.  In case the
+             keyblock has no packets at all and thus the loop was not
+             entered the above count_uids_with_flag would have
+             detected this case.  */
+
          u32 now = make_timestamp ();
 
          if (primary_pk->expiredate <= now)
@@ -1007,7 +1587,7 @@ sign_uids (estream_t fp,
              PKT_signature *sig;
              struct sign_attrib attrib;
 
-             assert (primary_pk);
+             log_assert (primary_pk);
              memset (&attrib, 0, sizeof attrib);
              attrib.non_exportable = local;
              attrib.non_revocable = nonrevocable;
@@ -1040,6 +1620,7 @@ sign_uids (estream_t fp,
                                          NULL);
              if (rc)
                {
+                  write_status_error ("keysig", rc);
                  log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                  goto leave;
                }
@@ -1111,7 +1692,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
           err = hexkeygrip_from_pk (pk, &hexgrip);
           if (err)
             goto leave;
-          err = agent_get_keyinfo (ctrl, hexgrip, &serialno);
+          err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
           if (!err && serialno)
             ; /* Key on card.  */
           else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@@ -1133,7 +1714,7 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
     }
 
   /* Change the passphrase for all keys.  */
-  for (any = 0, node = keyblock; node; node = node->next)
+  for (node = keyblock; node; node = node->next)
     {
       if (node->pkt->pkttype == PKT_PUBLIC_KEY
          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
@@ -1149,7 +1730,8 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
             goto leave;
 
           desc = gpg_format_keydesc (pk, FORMAT_KEYDESC_NORMAL, 1);
-          err = agent_passwd (ctrl, hexgrip, desc, &cache_nonce, &passwd_nonce);
+          err = agent_passwd (ctrl, hexgrip, desc, 0,
+                              &cache_nonce, &passwd_nonce);
           xfree (desc);
 
           if (err)
@@ -1173,54 +1755,6 @@ change_passphrase (ctrl_t ctrl, kbnode_t keyblock)
 
 
 \f
-/*
- * There are some keys out (due to a bug in gnupg), where the sequence
- * of the packets is wrong.  This function fixes that.
- * Returns: true if the keyblock has been fixed.
- *
- * Note:  This function does not work if there is more than one user ID.
- */
-static int
-fix_key_signature_order (KBNODE keyblock)
-{
-  KBNODE node, last, subkey;
-  int fixed = 0;
-
-  /* Locate key signatures of class 0x10..0x13 behind sub key packets.  */
-  for (subkey = last = NULL, node = keyblock; node;
-       last = node, node = node->next)
-    {
-      switch (node->pkt->pkttype)
-       {
-       case PKT_PUBLIC_SUBKEY:
-       case PKT_SECRET_SUBKEY:
-         if (!subkey)
-           subkey = last; /* Actually it is the one before the subkey.  */
-         break;
-       case PKT_SIGNATURE:
-         if (subkey)
-           {
-             PKT_signature *sig = node->pkt->pkt.signature;
-             if (sig->sig_class >= 0x10 && sig->sig_class <= 0x13)
-               {
-                 log_info (_("moving a key signature to the correct place\n"));
-                 last->next = node->next;
-                 node->next = subkey->next;
-                 subkey->next = node;
-                 node = last;
-                 fixed = 1;
-               }
-           }
-         break;
-       default:
-         break;
-       }
-    }
-
-  return fixed;
-}
-
-
 /* Fix various problems in the keyblock.  Returns true if the keyblock
    was changed.  Note that a pointer to the keyblock must be given and
    the function may change it (i.e. replacing the first node).  */
@@ -1229,10 +1763,10 @@ fix_keyblock (kbnode_t *keyblockp)
 {
   int changed = 0;
 
-  if (fix_key_signature_order (*keyblockp))
-    changed++;
   if (collapse_uids (keyblockp))
     changed++;
+  if (check_all_keysigs (*keyblockp, 0, 1))
+    changed++;
   reorder_keyblock (*keyblockp);
   /* If we modified the keyblock, make sure the flags are right. */
   if (changed)
@@ -1298,14 +1832,14 @@ enum cmdids
   cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
   cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
   cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
-  cmdEXPIRE, cmdBACKSIGN,
+  cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN,
 #ifndef NO_TRUST_MODELS
   cmdENABLEKEY, cmdDISABLEKEY,
 #endif /*!NO_TRUST_MODELS*/
   cmdSHOWPREF,
   cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
-  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCHECKBKUPKEY,
-  cmdCLEAN, cmdMINIMIZE, cmdNOP
+  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD,
+  cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
 };
 
 static struct
@@ -1322,12 +1856,14 @@ static struct
   { "help", cmdHELP, 0, N_("show this help")},
   { "?", cmdHELP, 0, NULL},
   { "fpr", cmdFPR, 0, N_("show key fingerprint")},
+  { "grip", cmdGRIP, 0, N_("show the keygrip")},
   { "list", cmdLIST, 0, N_("list key and user IDs")},
   { "l", cmdLIST, 0, NULL},
   { "uid", cmdSELUID, 0, N_("select user ID N")},
   { "key", cmdSELKEY, 0, N_("select subkey N")},
   { "check", cmdCHECK, 0, N_("check signatures")},
   { "c", cmdCHECK, 0, NULL},
+  { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "cross-certify", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "backsign", cmdBACKSIGN, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK, NULL},
   { "sign", cmdSIGN, KEYEDIT_NOT_SK | KEYEDIT_TAIL_MATCH,
@@ -1355,7 +1891,6 @@ static struct
     N_("move a key to a smartcard")},
   { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK,
     N_("move a backup key to a smartcard")},
-  { "checkbkupkey", cmdCHECKBKUPKEY, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK, NULL},
 #endif /*ENABLE_CARD_SUPPORT */
   { "delkey", cmdDELKEY, KEYEDIT_NOT_SK, N_("delete selected subkeys")},
   { "addrevoker", cmdADDREVOKER, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK,
@@ -1366,8 +1901,7 @@ static struct
     N_("change the expiration date for the key or selected subkeys")},
   { "primary", cmdPRIMARY, KEYEDIT_NOT_SK | KEYEDIT_NEED_SK,
     N_("flag the selected user ID as primary")},
-  { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK,
-    N_("toggle between the secret and public key listings")},
+  { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK, NULL},  /* Dummy command.  */
   { "t", cmdTOGGLE, KEYEDIT_NEED_SK, NULL},
   { "pref", cmdPREF, KEYEDIT_NOT_SK, N_("list preferences (expert)")},
   { "showpref", cmdSHOWPREF, KEYEDIT_NOT_SK, N_("list preferences (verbose)")},
@@ -1472,7 +2006,6 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
   int modified = 0;
   int sec_shadowing = 0;
   int run_subkey_warnings = 0;
-  int toggle;
   int have_commands = !!commands;
 
   if (opt.command_fd != -1)
@@ -1493,7 +2026,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
      and run the stale check as early as possible.  Note, that for
      non- W32 platforms it is run indirectly trough a call to
      get_validity ().  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 #endif
 
   /* Get the public key */
@@ -1515,8 +2048,6 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        tty_printf (_("Secret key is available.\n"));
     }
 
-  toggle = 0;
-
   /* Main command loop.  */
   for (;;)
     {
@@ -1529,7 +2060,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 
       if (redisplay && !quiet)
        {
-         show_key_with_all_names (NULL, keyblock, 0, 1, 0, 1, 0, 0);
+          /* Show using flags: with_revoker, with_subkeys.  */
+         show_key_with_all_names (ctrl, NULL, keyblock, 0, 1, 0, 1, 0, 0);
          tty_printf ("\n");
          redisplay = 0;
        }
@@ -1616,13 +2148,6 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
              tty_printf (_("Need the secret key to do this.\n"));
              cmd = cmdNOP;
            }
-         else if (((cmds[i].flags & KEYEDIT_NOT_SK) && have_seckey && toggle)
-                  || ((cmds[i].flags & KEYEDIT_ONLY_SK) && have_seckey
-                      && !toggle))
-           {
-             tty_printf (_("Please use the command \"toggle\" first.\n"));
-             cmd = cmdNOP;
-           }
          else
            cmd = cmds[i].id;
        }
@@ -1654,7 +2179,13 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdFPR:
-         show_key_and_fingerprint (keyblock);
+         show_key_and_fingerprint
+            (keyblock, (*arg_string == '*'
+                        && (!arg_string[1] || spacep (arg_string + 1))));
+         break;
+
+       case cmdGRIP:
+         show_key_and_grip (keyblock);
          break;
 
        case cmdSELUID:
@@ -1674,13 +2205,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            if (*arg_string == '*'
                && (!arg_string[1] || spacep (arg_string + 1)))
              arg_number = -1;  /* Select all. */
-           if (menu_select_key (keyblock, arg_number))
+           if (menu_select_key (keyblock, arg_number, p))
              redisplay = 1;
          }
          break;
 
        case cmdCHECK:
-         check_all_keysigs (keyblock, count_selected_uids (keyblock));
+         if (check_all_keysigs (keyblock, count_selected_uids (keyblock),
+                                 !strcmp (arg_string, "selfsig")))
+            modified = 1;
          break;
 
        case cmdSIGN:
@@ -1706,21 +2239,31 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                  }
              }
 
-           if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock)
-               && !cpr_get_answer_is_yes ("keyedit.sign_all.okay",
-                                          _("Really sign all user IDs?"
-                                            " (y/N) ")))
-             {
-               if (opt.interactive)
-                 interactive = 1;
-               else
-                 {
-                   tty_printf (_("Hint: Select the user IDs to sign\n"));
-                   have_commands = 0;
-                   break;
-                 }
-
-             }
+           if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock))
+              {
+                int result;
+                if (opt.only_sign_text_ids)
+                  result = cpr_get_answer_is_yes
+                    ("keyedit.sign_all.okay",
+                     _("Really sign all user IDs? (y/N) "));
+                else
+                  result = cpr_get_answer_is_yes
+                    ("keyedit.sign_all.okay",
+                     _("Really sign all text user IDs? (y/N) "));
+
+                if (! result)
+                  {
+                    if (opt.interactive)
+                      interactive = 1;
+                    else
+                      {
+                        tty_printf (_("Hint: Select the user IDs to sign\n"));
+                        have_commands = 0;
+                        break;
+                      }
+
+                  }
+              }
            /* What sort of signing are we doing? */
            if (!parse_sign_type
                (answer, &localsig, &nonrevokesig, &trustsig))
@@ -1729,7 +2272,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                break;
              }
 
-           sign_uids (NULL, keyblock, locusr, &modified,
+           sign_uids (ctrl, NULL, keyblock, locusr, &modified,
                       localsig, nonrevokesig, trustsig, interactive, 0);
          }
          break;
@@ -1743,7 +2286,6 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
              where we worked with a secret and a public keyring.  It
              is not necessary anymore but we keep this command for the
              sake of scripts using it.  */
-         toggle = !toggle;
          redisplay = 1;
          break;
 
@@ -1757,7 +2299,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          photo = 1;
          /* fall through */
        case cmdADDUID:
-         if (menu_adduid (keyblock, photo, arg_string))
+         if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL))
            {
              update_trust = 1;
              redisplay = 1;
@@ -1771,7 +2313,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            int n1;
 
            if (!(n1 = count_selected_uids (keyblock)))
-             tty_printf (_("You must select at least one user ID.\n"));
+              {
+                tty_printf (_("You must select at least one user ID.\n"));
+                if (!opt.expert)
+                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
+              }
            else if (real_uids_left (keyblock) < 1)
              tty_printf (_("You can't delete the last user ID!\n"));
            else if (cpr_get_answer_is_yes
@@ -1791,7 +2337,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            int n1;
 
            if (!(n1 = count_selected_uids (keyblock)))
-             tty_printf (_("You must select at least one user ID.\n"));
+              {
+                tty_printf (_("You must select at least one user ID.\n"));
+                if (!opt.expert)
+                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
+              }
            else if (menu_delsig (keyblock))
              {
                /* No redisplay here, because it may scroll away some
@@ -1802,7 +2352,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdADDKEY:
-         if (!generate_subkeypair (ctrl, keyblock))
+         if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL))
            {
              redisplay = 1;
              modified = 1;
@@ -1858,24 +2408,28 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
          break;
 
        case cmdBKUPTOCARD:
-       case cmdCHECKBKUPKEY:
-          log_debug ("FIXME: This needs to be changed\n");
          {
            /* Ask for a filename, check whether this is really a
               backup key as generated by the card generation, parse
               that key and store it on card. */
            KBNODE node;
-           const char *fname;
+           char *fname;
            PACKET *pkt;
            IOBUF a;
 
-           fname = arg_string;
-           if (!*fname)
+            if (!*arg_string)
              {
                tty_printf (_("Command expects a filename argument\n"));
                break;
              }
 
+            if (*arg_string == DIRSEP_C)
+              fname = xstrdup (arg_string);
+            else if (*arg_string == '~')
+              fname = make_filename (arg_string, NULL);
+            else
+              fname = make_filename (gnupg_homedir (), arg_string, NULL);
+
            /* Open that file.  */
            a = iobuf_open (fname);
            if (a && is_secured_file (iobuf_get_fd (a)))
@@ -1884,12 +2438,13 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
                a = NULL;
                gpg_err_set_errno (EPERM);
              }
-           if (!a)
-             {
-               tty_printf (_("Can't open '%s': %s\n"),
-                           fname, strerror (errno));
-               break;
-             }
+            if (!a)
+              {
+                tty_printf (_("Can't open '%s': %s\n"),
+                            fname, strerror (errno));
+                xfree (fname);
+                break;
+              }
 
            /* Parse and check that file.  */
            pkt = xmalloc (sizeof *pkt);
@@ -1900,49 +2455,34 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            if (!err && pkt->pkttype != PKT_SECRET_KEY
                && pkt->pkttype != PKT_SECRET_SUBKEY)
              err = GPG_ERR_NO_SECKEY;
-           if (err)
-             {
-               tty_printf (_("Error reading backup key from '%s': %s\n"),
-                           fname, gpg_strerror (err));
-               free_packet (pkt);
-               xfree (pkt);
-               break;
-             }
+            if (err)
+              {
+                tty_printf (_("Error reading backup key from '%s': %s\n"),
+                            fname, gpg_strerror (err));
+                xfree (fname);
+                free_packet (pkt);
+                xfree (pkt);
+                break;
+              }
+
+           xfree (fname);
            node = new_kbnode (pkt);
 
-           if (cmd == cmdCHECKBKUPKEY)
-             {
-               /* PKT_public_key *sk = node->pkt->pkt.secret_key; */
-               /* switch (is_secret_key_protected (sk)) */
-               /*   { */
-               /*   case 0:    /\* Not protected. *\/ */
-               /*     tty_printf (_("This key is not protected.\n")); */
-               /*     break; */
-               /*   case -1: */
-               /*     log_error (_("unknown key protection algorithm\n")); */
-               /*     break; */
-               /*   default: */
-               /*     if (sk->protect.s2k.mode == 1001) */
-               /*       tty_printf (_("Secret parts of key" */
-               /*                  " are not available.\n")); */
-               /*     if (sk->protect.s2k.mode == 1002) */
-               /*       tty_printf (_("Secret parts of key" */
-               /*                  " are stored on-card.\n")); */
-                   /* else */
-                   /*   check_secret_key (sk, 0); */
-                 /* } */
-             }
-           else                /* Store it.  */
-             {
-               if (card_store_subkey (node, 0))
-                 {
-                   redisplay = 1;
-                   sec_shadowing = 1;
-                 }
-             }
-           release_kbnode (node);
-         }
-         break;
+            /* Transfer it to gpg-agent which handles secret keys.  */
+            err = transfer_secret_keys (ctrl, NULL, node, 1, 1);
+
+            /* Treat the pkt as a public key.  */
+            pkt->pkttype = PKT_PUBLIC_KEY;
+
+            /* Ask gpg-agent to store the secret key to card.  */
+            if (card_store_subkey (node, 0))
+              {
+                redisplay = 1;
+                sec_shadowing = 1;
+              }
+            release_kbnode (node);
+          }
+          break;
 
 #endif /* ENABLE_CARD_SUPPORT */
 
@@ -1951,7 +2491,11 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            int n1;
 
            if (!(n1 = count_selected_keys (keyblock)))
-             tty_printf (_("You must select at least one key.\n"));
+              {
+                tty_printf (_("You must select at least one key.\n"));
+                if (!opt.expert)
+                  tty_printf (_("(Use the '%s' command.)\n"), "key");
+              }
            else if (!cpr_get_answer_is_yes
                      ("keyedit.remove.subkey.okay",
                       n1 > 1 ? _("Do you really want to delete the "
@@ -1987,13 +2531,17 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            int n1;
 
            if (!(n1 = count_selected_uids (keyblock)))
-             tty_printf (_("You must select at least one user ID.\n"));
+              {
+                tty_printf (_("You must select at least one user ID.\n"));
+                if (!opt.expert)
+                  tty_printf (_("(Use the '%s' command.)\n"), "uid");
+              }
            else if (cpr_get_answer_is_yes
                      ("keyedit.revoke.uid.okay",
                       n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ")
                      :        _("Really revoke this user ID? (y/N) ")))
              {
-               if (menu_revuid (keyblock))
+               if (menu_revuid (ctrl, keyblock))
                  {
                    modified = 1;
                    redisplay = 1;
@@ -2046,6 +2594,15 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
            }
          break;
 
+       case cmdCHANGEUSAGE:
+         if (menu_changeusage (keyblock))
+           {
+             merge_keys_and_selfsig (keyblock);
+             modified = 1;
+             redisplay = 1;
+           }
+         break;
+
        case cmdBACKSIGN:
          if (menu_backsign (keyblock))
            {
@@ -2076,9 +2633,9 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
              break;
            }
 
-         show_key_with_all_names (NULL, keyblock, 0, 0, 0, 1, 0, 0);
+         show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0);
          tty_printf ("\n");
-         if (edit_ownertrust (find_kbnode (keyblock,
+         if (edit_ownertrust (ctrl, find_kbnode (keyblock,
                                            PKT_PUBLIC_KEY)->pkt->pkt.
                               public_key, 1))
            {
@@ -2094,8 +2651,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        case cmdPREF:
          {
            int count = count_selected_uids (keyblock);
-           assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
-           show_names (NULL, keyblock, keyblock->pkt->pkt.public_key,
+           log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+           show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
                        count ? NODFLG_SELUID : 0, 1);
          }
          break;
@@ -2103,8 +2660,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
        case cmdSHOWPREF:
          {
            int count = count_selected_uids (keyblock);
-           assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
-           show_names (NULL, keyblock, keyblock->pkt->pkt.public_key,
+           log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+           show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key,
                        count ? NODFLG_SELUID : 0, 2);
          }
          break;
@@ -2180,7 +2737,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
 #endif /*!NO_TRUST_MODELS*/
 
        case cmdSHOWPHOTO:
-         menu_showphoto (keyblock);
+         menu_showphoto (ctrl, keyblock);
          break;
 
        case cmdCLEAN:
@@ -2268,33 +2825,194 @@ keyedit_passwd (ctrl_t ctrl, const char *username)
       err = gpg_error_from_syserror ();
       goto leave;
     }
-  err = getkey_byname (NULL, pk, username, 1, &keyblock);
+  err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock);
   if (err)
     goto leave;
 
-  err = change_passphrase (ctrl, keyblock);
+  err = change_passphrase (ctrl, keyblock);
+
+leave:
+  release_kbnode (keyblock);
+  free_public_key (pk);
+  if (err)
+    {
+      log_info ("error changing the passphrase for '%s': %s\n",
+               username, gpg_strerror (err));
+      write_status_error ("keyedit.passwd", err);
+    }
+  else
+    write_status_text (STATUS_SUCCESS, "keyedit.passwd");
+}
+
+
+/* Unattended adding of a new keyid.  USERNAME specifies the
+   key. NEWUID is the new user id to add to the key.  */
+void
+keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE kdbhd = NULL;
+  KEYDB_SEARCH_DESC desc;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
+  char *uidstring = NULL;
+
+  uidstring = xstrdup (newuid);
+  trim_spaces (uidstring);
+  if (!*uidstring)
+    {
+      log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID));
+      goto leave;
+    }
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* Search the key; we don't want the whole getkey stuff here.  */
+  kdbhd = keydb_new ();
+  if (!kdbhd)
+    {
+      /* Note that keydb_new has already used log_error.  */
+      goto leave;
+    }
+
+  err = classify_user_id (username, &desc, 1);
+  if (!err)
+    err = keydb_search (kdbhd, &desc, 1, NULL);
+  if (!err)
+    {
+      err = keydb_get_keyblock (kdbhd, &keyblock);
+      if (err)
+        {
+          log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+      /* Now with the keyblock retrieved, search again to detect an
+         ambiguous specification.  We need to save the found state so
+         that we can do an update later.  */
+      keydb_push_found_state (kdbhd);
+      err = keydb_search (kdbhd, &desc, 1, NULL);
+      if (!err)
+        err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+      else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
+        err = 0;
+      keydb_pop_found_state (kdbhd);
+
+      if (!err)
+        {
+          /* We require the secret primary key to add a UID.  */
+          node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
+          if (!node)
+            BUG ();
+          err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key);
+        }
+    }
+  if (err)
+    {
+      log_error (_("secret key \"%s\" not found: %s\n"),
+                 username, gpg_strerror (err));
+      goto leave;
+    }
+
+  fix_keyblock (&keyblock);
+
+  if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring))
+    {
+      err = keydb_update_keyblock (kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+
+      if (update_trust)
+        revalidation_mark ();
+    }
+
+ leave:
+  xfree (uidstring);
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
+/* Find a keyblock by fingerprint because only this uniquely
+ * identifies a key and may thus be used to select a key for
+ * unattended subkey creation os key signing.  */
+static gpg_error_t
+find_by_primary_fpr (ctrl_t ctrl, const char *fpr,
+                     kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd)
+{
+  gpg_error_t err;
+  kbnode_t keyblock = NULL;
+  KEYDB_HANDLE kdbhd = NULL;
+  KEYDB_SEARCH_DESC desc;
+  byte fprbin[MAX_FINGERPRINT_LEN];
+  size_t fprlen;
+
+  *r_keyblock = NULL;
+  *r_kdbhd = NULL;
+
+  if (classify_user_id (fpr, &desc, 1)
+      || !(desc.mode == KEYDB_SEARCH_MODE_FPR
+           || desc.mode == KEYDB_SEARCH_MODE_FPR16
+           || desc.mode == KEYDB_SEARCH_MODE_FPR20))
+    {
+      log_error (_("\"%s\" is not a fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+  err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
+  if (err)
+    {
+      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Check that the primary fingerprint has been given. */
+  fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
+  if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
+      && !memcmp (fprbin, desc.u.fpr, 16))
+    ;
+  else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
+           && !memcmp (fprbin, desc.u.fpr, 16)
+           && !desc.u.fpr[16]
+           && !desc.u.fpr[17]
+           && !desc.u.fpr[18]
+           && !desc.u.fpr[19])
+    ;
+  else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
+                            || desc.mode == KEYDB_SEARCH_MODE_FPR)
+           && !memcmp (fprbin, desc.u.fpr, 20))
+    ;
+  else
+    {
+      log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
+      err = gpg_error (GPG_ERR_INV_NAME);
+      goto leave;
+    }
+
+  *r_keyblock = keyblock;
+  keyblock = NULL;
+  *r_kdbhd = kdbhd;
+  kdbhd = NULL;
+  err = 0;
 
-leave:
+ leave:
   release_kbnode (keyblock);
-  free_public_key (pk);
-  if (err)
-    {
-      log_info ("error changing the passphrase for '%s': %s\n",
-               username, gpg_strerror (err));
-      write_status_error ("keyedit.passwd", err);
-    }
-  else
-    write_status_text (STATUS_SUCCESS, "keyedit.passwd");
+  keydb_release (kdbhd);
+  return err;
 }
 
 
 /* Unattended key signing function.  If the key specifified by FPR is
-   availabale and FPR is the primary fingerprint all user ids of the
-   user ids of the key are signed using the default signing key.  If
-   UIDS is an empty list all usable UIDs are signed, if it is not
-   empty, only those user ids matching one of the entries of the loist
-   are signed.  With LOCAL being true kthe signatures are marked as
-   non-exportable.  */
+   available and FPR is the primary fingerprint all user ids of the
+   key are signed using the default signing key.  If UIDS is an empty
+   list all usable UIDs are signed, if it is not empty, only those
+   user ids matching one of the entries of the list are signed.  With
+   LOCAL being true the signatures are marked as non-exportable.  */
 void
 keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
                     strlist_t locusr, int local)
@@ -2303,7 +3021,6 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   kbnode_t keyblock = NULL;
   KEYDB_HANDLE kdbhd = NULL;
   int modified = 0;
-  KEYDB_SEARCH_DESC desc;
   PKT_public_key *pk;
   kbnode_t node;
   strlist_t sl;
@@ -2311,53 +3028,14 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
 
 #ifdef HAVE_W32_SYSTEM
   /* See keyedit_menu for why we need this.  */
-  check_trustdb_stale ();
+  check_trustdb_stale (ctrl);
 #endif
 
   /* We require a fingerprint because only this uniquely identifies a
      key and may thus be used to select a key for unattended key
      signing.  */
-  if (classify_user_id (fpr, &desc, 1)
-      || !(desc.mode == KEYDB_SEARCH_MODE_FPR
-           || desc.mode == KEYDB_SEARCH_MODE_FPR16
-           || desc.mode == KEYDB_SEARCH_MODE_FPR20))
-    {
-      log_error (_("\"%s\" is not a fingerprint\n"), fpr);
-      goto leave;
-    }
-  err = get_pubkey_byname (ctrl, NULL, NULL, fpr, &keyblock, &kdbhd, 1, 1);
-  if (err)
-    {
-      log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err));
-      goto leave;
-    }
-
-  /* Check that the primary fingerprint has been given. */
-  {
-    byte fprbin[MAX_FINGERPRINT_LEN];
-    size_t fprlen;
-
-    fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen);
-    if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR16
-        && !memcmp (fprbin, desc.u.fpr, 16))
-      ;
-    else if (fprlen == 16 && desc.mode == KEYDB_SEARCH_MODE_FPR
-             && !memcmp (fprbin, desc.u.fpr, 16)
-             && !desc.u.fpr[16]
-             && !desc.u.fpr[17]
-             && !desc.u.fpr[18]
-             && !desc.u.fpr[19])
-      ;
-    else if (fprlen == 20 && (desc.mode == KEYDB_SEARCH_MODE_FPR20
-                              || desc.mode == KEYDB_SEARCH_MODE_FPR)
-             && !memcmp (fprbin, desc.u.fpr, 20))
-      ;
-    else
-      {
-        log_error (_("\"%s\" is not the primary fingerprint\n"), fpr);
-        goto leave;
-      }
-  }
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
 
   if (fix_keyblock (&keyblock))
     modified++;
@@ -2365,7 +3043,7 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   /* Give some info in verbose.  */
   if (opt.verbose)
     {
-      show_key_with_all_names (es_stdout, keyblock, 0,
+      show_key_with_all_names (ctrl, es_stdout, keyblock, 0,
                                1/*with_revoker*/, 1/*with_fingerprint*/,
                                0, 0, 1);
       es_fflush (es_stdout);
@@ -2375,7 +3053,7 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   if (pk->flags.revoked)
     {
       if (!opt.verbose)
-        show_key_with_all_names (es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
       log_error ("%s%s", _("Key is revoked."), _("  Unable to sign.\n"));
       goto leave;
     }
@@ -2387,33 +3065,78 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
   menu_select_uid (keyblock, 0);   /* Better clear the flags first. */
   for (sl=uids; sl; sl = sl->next)
     {
+      const char *name = sl->d;
+      int count = 0;
+
+      sl->flags &= ~(1|2);  /* Clear flags used for error reporting.  */
+
       for (node = keyblock; node; node = node->next)
         {
           if (node->pkt->pkttype == PKT_USER_ID)
             {
               PKT_user_id *uid = node->pkt->pkt.user_id;
 
-              if (!uid->attrib_data
-                  && ascii_memistr (uid->name, uid->len, sl->d))
+              if (uid->attrib_data)
+                ;
+              else if (*name == '='
+                       && strlen (name+1) == uid->len
+                       && !memcmp (uid->name, name + 1, uid->len))
+                { /* Exact match - we don't do a check for ambiguity
+                   * in this case.  */
+                  node->flag |= NODFLG_SELUID;
+                  if (any != -1)
+                    {
+                      sl->flags |= 1;  /* Report as found.  */
+                      any = 1;
+                    }
+                }
+              else if (ascii_memistr (uid->name, uid->len,
+                                      *name == '*'? name+1:name))
                 {
                   node->flag |= NODFLG_SELUID;
-                  any = 1;
+                  if (any != -1)
+                    {
+                      sl->flags |= 1;  /* Report as found.  */
+                      any = 1;
+                    }
+                  count++;
                 }
             }
         }
+
+      if (count > 1)
+        {
+          any = -1;        /* Force failure at end.  */
+          sl->flags |= 2;  /* Report as ambiguous.  */
+        }
     }
 
-  if (uids && !any)
+  /* Check whether all given user ids were found.  */
+  for (sl=uids; sl; sl = sl->next)
+    if (!(sl->flags & 1))
+      any = -1;  /* That user id was not found.  */
+
+  /* Print an error if there was a problem with the user ids.  */
+  if (uids && any < 1)
     {
       if (!opt.verbose)
-        show_key_with_all_names (es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
       es_fflush (es_stdout);
+      for (sl=uids; sl; sl = sl->next)
+        {
+          if ((sl->flags & 2))
+            log_info (_("Invalid user ID '%s': %s\n"),
+                      sl->d, gpg_strerror (GPG_ERR_AMBIGUOUS_NAME));
+          else if (!(sl->flags & 1))
+            log_info (_("Invalid user ID '%s': %s\n"),
+                      sl->d, gpg_strerror (GPG_ERR_NOT_FOUND));
+        }
       log_error ("%s  %s", _("No matching user IDs."), _("Nothing to sign.\n"));
       goto leave;
     }
 
   /* Sign. */
-  sign_uids (es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1);
+  sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1);
   es_fflush (es_stdout);
 
   if (modified)
@@ -2438,6 +3161,67 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids,
 }
 
 
+/* Unattended subkey creation function.
+ *
+ */
+void
+keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr,
+                      const char *usagestr, const char *expirestr)
+{
+  gpg_error_t err;
+  kbnode_t keyblock;
+  KEYDB_HANDLE kdbhd;
+  int modified = 0;
+  PKT_public_key *pk;
+
+#ifdef HAVE_W32_SYSTEM
+  /* See keyedit_menu for why we need this.  */
+  check_trustdb_stale (ctrl);
+#endif
+
+  /* We require a fingerprint because only this uniquely identifies a
+   * key and may thus be used to select a key for unattended subkey
+   * creation.  */
+  if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd))
+    goto leave;
+
+  if (fix_keyblock (&keyblock))
+    modified++;
+
+  pk = keyblock->pkt->pkt.public_key;
+  if (pk->flags.revoked)
+    {
+      if (!opt.verbose)
+        show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1);
+      log_error ("%s%s", _("Key is revoked."), "\n");
+      goto leave;
+    }
+
+  /* Create the subkey.  Noet that the called function already prints
+   * an error message. */
+  if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr))
+    modified = 1;
+  es_fflush (es_stdout);
+
+  /* Store.  */
+  if (modified)
+    {
+      err = keydb_update_keyblock (kdbhd, keyblock);
+      if (err)
+        {
+          log_error (_("update failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+    }
+  else
+    log_info (_("Key not changed so no update needed.\n"));
+
+ leave:
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+}
+
+
 \f
 static void
 tty_print_notations (int indent, PKT_signature * sig)
@@ -2639,12 +3423,13 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose)
    opt.with_colons is used.  It prints all available data in a easy to
    parse format and does not translate utf8 */
 static void
-show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
+show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
 {
   KBNODE node;
   int i, j, ulti_hack = 0;
   byte pk_version = 0;
   PKT_public_key *primary = NULL;
+  int have_seckey;
 
   if (!fp)
     fp = es_stdout;
@@ -2665,9 +3450,13 @@ show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
            }
 
          keyid_from_pk (pk, keyid);
+          have_seckey = !agent_probe_secret_key (ctrl, pk);
+
+          if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+            es_fputs (have_seckey? "sec:" : "pub:", fp);
+          else
+            es_fputs (have_seckey? "ssb:" : "sub:", fp);
 
-         es_fputs (node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub:" : "sub:",
-                    fp);
          if (!pk->flags.valid)
            es_putc ('i', fp);
          else if (pk->flags.revoked)
@@ -2676,7 +3465,7 @@ show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
            es_putc ('e', fp);
          else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks))
            {
-             int trust = get_validity_info (pk, NULL);
+             int trust = get_validity_info (ctrl, pk, NULL);
              if (trust == 'u')
                ulti_hack = 1;
              es_putc (trust, fp);
@@ -2735,7 +3524,7 @@ show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
              int uid_validity;
 
              if (primary && !ulti_hack)
-               uid_validity = get_validity_info (primary, uid);
+               uid_validity = get_validity_info (ctrl, primary, uid);
              else
                uid_validity = 'u';
              es_fprintf (fp, "%c::::::::", uid_validity);
@@ -2785,6 +3574,16 @@ show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
          if ((node->flag & NODFLG_MARK_A))
            es_putc ('m', fp);
          es_putc (':', fp);
+         if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)
+           {
+#ifdef USE_TOFU
+             enum tofu_policy policy;
+             if (! tofu_get_policy (ctrl, primary, uid, &policy)
+                 && policy != TOFU_POLICY_NONE)
+               es_fprintf (fp, "%s", tofu_policy_str (policy));
+#endif /*USE_TOFU*/
+           }
+         es_putc (':', fp);
          es_putc ('\n', fp);
        }
     }
@@ -2792,8 +3591,8 @@ show_key_with_all_names_colon (estream_t fp, kbnode_t keyblock)
 
 
 static void
-show_names (estream_t fp,
-            KBNODE keyblock, PKT_public_key * pk, unsigned int flag,
+show_names (ctrl_t ctrl, estream_t fp,
+            kbnode_t keyblock, PKT_public_key * pk, unsigned int flag,
            int with_prefs)
 {
   KBNODE node;
@@ -2808,7 +3607,7 @@ show_names (estream_t fp,
          if (!flag || (flag && (node->flag & flag)))
            {
              if (!(flag & NODFLG_MARK_A) && pk)
-               tty_fprintf (fp, "%s ", uid_trust_string_fixed (pk, uid));
+               tty_fprintf (fp, "%s ", uid_trust_string_fixed (ctrl, pk, uid));
 
              if (flag & NODFLG_MARK_A)
                tty_fprintf (fp, "     ");
@@ -2858,20 +3657,23 @@ show_names (estream_t fp,
  * tty (ignored in with-colons mode).
  */
 static void
-show_key_with_all_names (estream_t fp,
+show_key_with_all_names (ctrl_t ctrl, estream_t fp,
                          KBNODE keyblock, int only_marked, int with_revoker,
                         int with_fpr, int with_subkeys, int with_prefs,
                          int nowarn)
 {
-  KBNODE node;
+  gpg_error_t err;
+  kbnode_t node;
   int i;
   int do_warn = 0;
+  int have_seckey = 0;
+  char *serialno = NULL;
   PKT_public_key *primary = NULL;
   char pkstrbuf[PUBKEY_STRING_SIZE];
 
   if (opt.with_colons)
     {
-      show_key_with_all_names_colon (fp, keyblock);
+      show_key_with_all_names_colon (ctrl, fp, keyblock);
       return;
     }
 
@@ -2892,12 +3694,13 @@ show_key_with_all_names (estream_t fp,
               * output */
              static int did_warn = 0;
 
-             trust = get_validity_string (pk, NULL);
+             trust = get_validity_string (ctrl, pk, NULL);
              otrust = get_ownertrust_string (pk);
 
              /* Show a warning once */
              if (!did_warn
-                 && (get_validity (pk, NULL) & TRUST_FLAG_PENDING_CHECK))
+                 && (get_validity (ctrl, pk, NULL, NULL, 0)
+                     & TRUST_FLAG_PENDING_CHECK))
                {
                  did_warn = 1;
                  do_warn = 1;
@@ -2949,13 +3752,33 @@ show_key_with_all_names (estream_t fp,
            }
 
          keyid_from_pk (pk, NULL);
-         tty_fprintf (fp, "%s%c %s/%s",
-                     node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" :
-                     node->pkt->pkttype == PKT_PUBLIC_SUBKEY ? "sub" :
-                     node->pkt->pkttype == PKT_SECRET_KEY ? "sec" : "ssb",
-                     (node->flag & NODFLG_SELKEY) ? '*' : ' ',
-                      pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
-                     keystr (pk->keyid));
+
+          xfree (serialno);
+          serialno = NULL;
+          {
+            char *hexgrip;
+
+            err = hexkeygrip_from_pk (pk, &hexgrip);
+            if (err)
+              {
+                log_error ("error computing a keygrip: %s\n",
+                           gpg_strerror (err));
+                have_seckey = 0;
+              }
+            else
+              have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL);
+            xfree (hexgrip);
+          }
+
+         tty_fprintf
+            (fp, "%s%c %s/%s",
+             node->pkt->pkttype == PKT_PUBLIC_KEY && have_seckey? "sec" :
+             node->pkt->pkttype == PKT_PUBLIC_KEY ?               "pub" :
+             have_seckey ?                                        "ssb" :
+                                                                  "sub",
+             (node->flag & NODFLG_SELKEY) ? '*' : ' ',
+             pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+             keystr (pk->keyid));
 
           if (opt.legacy_list_mode)
             tty_fprintf (fp, "  ");
@@ -2974,10 +3797,30 @@ show_key_with_all_names (estream_t fp,
          tty_fprintf (fp, _("usage: %s"), usagestr_from_pk (pk, 1));
          tty_fprintf (fp, "\n");
 
-         if (pk->seckey_info
+          if (serialno)
+            {
+              /* The agent told us that a secret key is available and
+                 that it has been stored on a card.  */
+             tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "",
+                           _("card-no: "));
+              if (strlen (serialno) == 32
+                  && !strncmp (serialno, "D27600012401", 12))
+                {
+                  /* This is an OpenPGP card.  Print the relevant part.  */
+                  /* Example: D2760001240101010001000003470000 */
+                  /*                          xxxxyyyyyyyy     */
+                  tty_fprintf (fp, "%.*s %.*s\n",
+                               4, serialno+16, 8, serialno+20);
+                }
+              else
+                tty_fprintf (fp, "%s\n", serialno);
+
+            }
+         else if (pk->seckey_info
               && pk->seckey_info->is_protected
               && pk->seckey_info->s2k.mode == 1002)
            {
+              /* FIXME: Check wether this code path is still used.  */
              tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "",
                            _("card-no: "));
              if (pk->seckey_info->ivlen == 16
@@ -3010,9 +3853,10 @@ show_key_with_all_names (estream_t fp,
                                opt.legacy_list_mode?
                                ((int) keystrlen () + 13):5, "");
                  /* Ownertrust is only meaningful for the PGP or
-                    classic trust models */
+                    classic trust models, or PGP combined with TOFU */
                  if (opt.trust_model == TM_PGP
-                     || opt.trust_model == TM_CLASSIC)
+                     || opt.trust_model == TM_CLASSIC
+                     || opt.trust_model == TM_TOFU_PGP)
                    {
                      int width = 14 - strlen (otrust);
                      if (width <= 0)
@@ -3042,20 +3886,24 @@ show_key_with_all_names (estream_t fp,
        }
     }
 
-  show_names (fp,
+  show_names (ctrl, fp,
               keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs);
 
   if (do_warn && !nowarn)
     tty_fprintf (fp, _("Please note that the shown key validity"
                        " is not necessarily correct\n"
                        "unless you restart the program.\n"));
+
+  xfree (serialno);
 }
 
 
 /* Display basic key information.  This function is suitable to show
    information on the key without any dependencies on the trustdb or
    any other internal GnuPG stuff.  KEYBLOCK may either be a public or
-   a secret key.*/
+   a secret key.  This function may be called with KEYBLOCK containing
+   secret keys and thus the printing of "pub" vs. "sec" does only
+   depend on the packet type and not by checking with gpg-agent.  */
 void
 show_basic_key_info (KBNODE keyblock)
 {
@@ -3107,10 +3955,11 @@ show_basic_key_info (KBNODE keyblock)
     }
 }
 
+
 static void
-show_key_and_fingerprint (KBNODE keyblock)
+show_key_and_fingerprint (kbnode_t keyblock, int with_subkeys)
 {
-  KBNODE node;
+  kbnode_t node;
   PKT_public_key *pk = NULL;
   char pkstrbuf[PUBKEY_STRING_SIZE];
 
@@ -3134,6 +3983,56 @@ show_key_and_fingerprint (KBNODE keyblock)
   tty_printf ("\n");
   if (pk)
     print_fingerprint (NULL, pk, 2);
+  if (with_subkeys)
+    {
+      for (node = keyblock; node; node = node->next)
+        {
+          if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+            {
+              pk = node->pkt->pkt.public_key;
+              tty_printf ("sub   %s/%s %s [%s]\n",
+                          pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+                          keystr_from_pk(pk),
+                          datestr_from_pk (pk),
+                          usagestr_from_pk (pk, 0));
+
+              print_fingerprint (NULL, pk, 4);
+            }
+        }
+    }
+}
+
+
+/* Show a listing of the primary and its subkeys along with their
+   keygrips.  */
+static void
+show_key_and_grip (kbnode_t keyblock)
+{
+  kbnode_t node;
+  PKT_public_key *pk = NULL;
+  char pkstrbuf[PUBKEY_STRING_SIZE];
+  char *hexgrip;
+
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_PUBLIC_KEY
+          || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+        {
+          pk = node->pkt->pkt.public_key;
+          tty_printf ("%s   %s/%s %s [%s]\n",
+                      node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+                      pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
+                      keystr_from_pk(pk),
+                      datestr_from_pk (pk),
+                      usagestr_from_pk (pk, 0));
+
+          if (!hexkeygrip_from_pk (pk, &hexgrip))
+            {
+              tty_printf ("      Keygrip: %s\n", hexgrip);
+              xfree (hexgrip);
+            }
+        }
+    }
 }
 
 
@@ -3225,11 +4124,15 @@ subkey_expire_warning (kbnode_t keyblock)
 
 
 /*
- * Ask for a new user id, add the self-signature and update the keyblock.
- * Return true if there is a new user id
+ * Ask for a new user id, add the self-signature, and update the
+ * keyblock.  If UIDSTRING is not NULL the user ID is generated
+ * unattended using that string.  UIDSTRING is expected to be utf-8
+ * encoded and white space trimmed.  Returns true if there is a new
+ * user id.
  */
 static int
-menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
+menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock,
+             int photo, const char *photo_name, const char *uidstring)
 {
   PKT_user_id *uid;
   PKT_public_key *pk = NULL;
@@ -3239,6 +4142,9 @@ menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
   KBNODE pub_where = NULL;
   gpg_error_t err;
 
+  if (photo && uidstring)
+    return 0;  /* Not allowed.  */
+
   for (node = pub_keyblock; node; pub_where = node, node = node->next)
     {
       if (node->pkt->pkttype == PKT_PUBLIC_KEY)
@@ -3248,7 +4154,7 @@ menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
     }
   if (!node) /* No subkey.  */
     pub_where = NULL;
-  assert (pk);
+  log_assert (pk);
 
   if (photo)
     {
@@ -3288,17 +4194,22 @@ menu_adduid (KBNODE pub_keyblock, int photo, const char *photo_name)
            }
        }
 
-      uid = generate_photo_id (pk, photo_name);
+      uid = generate_photo_id (ctrl, pk, photo_name);
     }
   else
-    uid = generate_user_id (pub_keyblock);
+    uid = generate_user_id (pub_keyblock, uidstring);
   if (!uid)
-    return 0;
+    {
+      if (uidstring)
+        log_error ("%s", _("Such a user ID already exists on this key!\n"));
+      return 0;
+    }
 
   err = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x13, 0, 0, 0,
                             keygen_add_std_prefs, pk, NULL);
   if (err)
     {
+      write_status_error ("keysig", err);
       log_error ("signing failed: %s\n", gpg_strerror (err));
       free_user_id (uid);
       return 0;
@@ -3385,7 +4296,7 @@ menu_delsig (KBNODE pub_keyblock)
          else
            valid = print_and_check_one_sig (pub_keyblock, node,
                                             &inv_sig, &no_key, &other_err,
-                                            &selfsig, 1);
+                                            &selfsig, 1, 0);
 
          if (valid)
            {
@@ -3428,8 +4339,8 @@ menu_delsig (KBNODE pub_keyblock)
   if (changed)
     {
       commit_kbnode (&pub_keyblock);
-      tty_printf (changed == 1 ? _("Deleted %d signature.\n")
-                 : _("Deleted %d signatures.\n"), changed);
+      tty_printf (ngettext("Deleted %d signature.\n",
+                           "Deleted %d signatures.\n", changed), changed);
     }
   else
     tty_printf (_("Nothing deleted.\n"));
@@ -3475,11 +4386,9 @@ menu_clean (KBNODE keyblock, int self_only)
            }
          else if (sigs)
            {
-             tty_printf (sigs == 1 ?
-                         _("User ID \"%s\": %d signature removed\n") :
-                         _("User ID \"%s\": %d signatures removed\n"),
-                         user, sigs);
-
+             tty_printf (ngettext("User ID \"%s\": %d signature removed\n",
+                                   "User ID \"%s\": %d signatures removed\n",
+                                   sigs), user, sigs);
              modified = 1;
            }
          else
@@ -3542,7 +4451,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
   size_t fprlen;
   int rc;
 
-  assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   pk = pub_keyblock->pkt->pkt.public_key;
 
@@ -3648,8 +4557,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
                  log_error (_("this key has already been designated "
                               "as a revoker\n"));
 
-                 sprintf (buf, "%08lX%08lX",
-                          (ulong) pk->keyid[0], (ulong) pk->keyid[1]);
+                  format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf));
                  write_status_text (STATUS_ALREADY_SIGNED, buf);
 
                  break;
@@ -3683,6 +4591,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
                           keygen_add_revkey, &revkey, NULL);
   if (rc)
     {
+      write_status_error ("keysig", rc);
       log_error ("signing failed: %s\n", gpg_strerror (rc));
       goto fail;
     }
@@ -3718,8 +4627,11 @@ menu_expire (KBNODE pub_keyblock)
   n1 = count_selected_keys (pub_keyblock);
   if (n1 > 1)
     {
-      tty_printf (_("Please select at most one subkey.\n"));
-      return 0;
+      if (!cpr_get_answer_is_yes
+          ("keyedit.expire_multiple_subkeys.okay",
+           _("Are you sure you want to change the"
+             " expiration time for multiple subkeys? (y/N) ")))
+       return 0;
     }
   else if (n1)
     tty_printf (_("Changing expiration time for a subkey.\n"));
@@ -3744,11 +4656,15 @@ menu_expire (KBNODE pub_keyblock)
          keyid_from_pk (main_pk, keyid);
          main_pk->expiredate = expiredate;
        }
-      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
-              && (node->flag & NODFLG_SELKEY))
+      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
        {
-         sub_pk = node->pkt->pkt.public_key;
-         sub_pk->expiredate = expiredate;
+          if (node->flag & NODFLG_SELKEY)
+            {
+              sub_pk = node->pkt->pkt.public_key;
+              sub_pk->expiredate = expiredate;
+            }
+          else
+            sub_pk = NULL;
        }
       else if (node->pkt->pkttype == PKT_USER_ID)
        uid = node->pkt->pkt.user_id;
@@ -3808,6 +4724,112 @@ menu_expire (KBNODE pub_keyblock)
 }
 
 
+/* Change the capability of a selected key.  This command should only
+ * be used to rectify badly created keys and as such is not suggested
+ * for general use.  */
+static int
+menu_changeusage (kbnode_t keyblock)
+{
+  int n1, rc;
+  int mainkey = 0;
+  PKT_public_key *main_pk, *sub_pk;
+  PKT_user_id *uid;
+  kbnode_t node;
+  u32 keyid[2];
+
+  n1 = count_selected_keys (keyblock);
+  if (n1 > 1)
+    {
+      tty_printf (_("You must select exactly one key.\n"));
+      return 0;
+    }
+  else if (n1)
+    tty_printf ("Changing usage of a subkey.\n");
+  else
+    {
+      tty_printf ("Changing usage of the primary key.\n");
+      mainkey = 1;
+    }
+
+  /* Now we can actually change the self-signature(s) */
+  main_pk = sub_pk = NULL;
+  uid = NULL;
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+       {
+         main_pk = node->pkt->pkt.public_key;
+         keyid_from_pk (main_pk, keyid);
+       }
+      else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+       {
+          if (node->flag & NODFLG_SELKEY)
+            sub_pk = node->pkt->pkt.public_key;
+          else
+            sub_pk = NULL;
+       }
+      else if (node->pkt->pkttype == PKT_USER_ID)
+       uid = node->pkt->pkt.user_id;
+      else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE
+              && (mainkey || sub_pk))
+       {
+         PKT_signature *sig = node->pkt->pkt.signature;
+         if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+             && ((mainkey && uid
+                  && uid->created && (sig->sig_class & ~3) == 0x10)
+                 || (!mainkey && sig->sig_class == 0x18))
+             && sig->flags.chosen_selfsig)
+           {
+             /* This is the self-signature which is to be replaced.  */
+             PKT_signature *newsig;
+             PACKET *newpkt;
+
+             if ((mainkey && main_pk->version < 4)
+                 || (!mainkey && sub_pk->version < 4))
+               {
+                 log_info ("You can't change the capabilities of a v3 key\n");
+                 return 0;
+               }
+
+              if (mainkey)
+                main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0,
+                                                       main_pk->pubkey_usage);
+              else
+                sub_pk->pubkey_usage  = ask_key_flags (sub_pk->pubkey_algo, 1,
+                                                       sub_pk->pubkey_usage);
+
+             if (mainkey)
+               rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL,
+                                          main_pk, keygen_add_key_flags,
+                                          main_pk);
+             else
+               rc =
+                 update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk,
+                                       main_pk, keygen_add_key_flags, sub_pk);
+             if (rc)
+               {
+                 log_error ("make_keysig_packet failed: %s\n",
+                            gpg_strerror (rc));
+                 return 0;
+               }
+
+             /* Replace the packet.  */
+             newpkt = xmalloc_clear (sizeof *newpkt);
+             newpkt->pkttype = PKT_SIGNATURE;
+             newpkt->pkt.signature = newsig;
+             free_packet (node->pkt);
+             xfree (node->pkt);
+             node->pkt = newpkt;
+             sub_pk = NULL;
+              break;
+           }
+       }
+    }
+
+  return 1;
+}
+
+
 static int
 menu_backsign (KBNODE pub_keyblock)
 {
@@ -3816,7 +4838,7 @@ menu_backsign (KBNODE pub_keyblock)
   KBNODE node;
   u32 timestamp;
 
-  assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   merge_keys_and_selfsig (pub_keyblock);
   main_pk = pub_keyblock->pkt->pkt.public_key;
@@ -4565,7 +5587,7 @@ menu_select_uid_namehash (KBNODE keyblock, const char *namehash)
   KBNODE node;
   int i;
 
-  assert (strlen (namehash) == NAMEHASH_LEN * 2);
+  log_assert (strlen (namehash) == NAMEHASH_LEN * 2);
 
   for (i = 0; i < NAMEHASH_LEN; i++)
     hash[i] = hextobyte (&namehash[i * 2]);
@@ -4603,10 +5625,97 @@ menu_select_uid_namehash (KBNODE keyblock, const char *namehash)
  * Returns: True if the selection changed.
  */
 static int
-menu_select_key (KBNODE keyblock, int idx)
+menu_select_key (KBNODE keyblock, int idx, char *p)
 {
   KBNODE node;
-  int i;
+  int i, j;
+  int is_hex_digits;
+
+  is_hex_digits = p && strlen (p) >= 8;
+  if (is_hex_digits)
+    {
+      /* Skip initial spaces.  */
+      while (spacep (p))
+        p ++;
+      /* If the id starts with 0x accept and ignore it.  */
+      if (p[0] == '0' && p[1] == 'x')
+        p += 2;
+
+      for (i = 0, j = 0; p[i]; i ++)
+        if (hexdigitp (&p[i]))
+          {
+            p[j] = toupper (p[i]);
+            j ++;
+          }
+        else if (spacep (&p[i]))
+          /* Skip spaces.  */
+          {
+          }
+        else
+          {
+            is_hex_digits = 0;
+            break;
+          }
+      if (is_hex_digits)
+        /* In case we skipped some spaces, add a new NUL terminator.  */
+        {
+          p[j] = 0;
+          /* If we skipped some spaces, make sure that we still have
+             at least 8 characters.  */
+          is_hex_digits = (/* Short keyid.  */
+                           strlen (p) == 8
+                           /* Long keyid.  */
+                           || strlen (p) == 16
+                           /* Fingerprints are (currently) 32 or 40
+                              characters.  */
+                           || strlen (p) >= 32);
+        }
+    }
+
+  if (is_hex_digits)
+    {
+      int found_one = 0;
+      for (node = keyblock; node; node = node->next)
+       if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+           || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+          {
+            int match = 0;
+            if (strlen (p) == 8 || strlen (p) == 16)
+              {
+                u32 kid[2];
+                char kid_str[17];
+                keyid_from_pk (node->pkt->pkt.public_key, kid);
+                format_keyid (kid, strlen (p) == 8 ? KF_SHORT : KF_LONG,
+                              kid_str, sizeof (kid_str));
+
+                if (strcmp (p, kid_str) == 0)
+                  match = 1;
+              }
+            else
+              {
+                char fp[2*MAX_FINGERPRINT_LEN + 1];
+                hexfingerprint (node->pkt->pkt.public_key, fp, sizeof (fp));
+                if (strcmp (fp, p) == 0)
+                  match = 1;
+              }
+
+            if (match)
+              {
+                if ((node->flag & NODFLG_SELKEY))
+                  node->flag &= ~NODFLG_SELKEY;
+                else
+                  node->flag |= NODFLG_SELKEY;
+
+                found_one = 1;
+              }
+          }
+
+      if (found_one)
+        return 1;
+
+      tty_printf (_("No subkey with key ID '%s'.\n"), p);
+      return 0;
+    }
 
   if (idx == -1)               /* Select all.  */
     {
@@ -4812,7 +5921,7 @@ menu_revsig (KBNODE keyblock)
   int rc, any, skip = 1, all = !count_selected_uids (keyblock);
   struct revocation_reason_info *reason = NULL;
 
-  assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+  log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
 
   /* First check whether we have any signatures at all.  */
   any = 0;
@@ -4953,7 +6062,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
          || node->pkt->pkttype != PKT_SIGNATURE)
        continue;
       unode = find_prev_kbnode (keyblock, node, PKT_USER_ID);
-      assert (unode);          /* we already checked this */
+      log_assert (unode); /* we already checked this */
 
       memset (&attrib, 0, sizeof attrib);
       attrib.reason = reason;
@@ -4974,6 +6083,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
       free_public_key (signerkey);
       if (rc)
        {
+          write_status_error ("keysig", rc);
          log_error (_("signing failed: %s\n"), gpg_strerror (rc));
          release_revocation_reason_info (reason);
          return changed;
@@ -4999,7 +6109,7 @@ reloop:                   /* (must use this, because we are modifing the list) */
 /* Revoke a user ID (i.e. revoke a user ID selfsig).  Return true if
    keyblock changed.  */
 static int
-menu_revuid (KBNODE pub_keyblock)
+menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock)
 {
   PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
   KBNODE node;
@@ -5065,6 +6175,7 @@ menu_revuid (KBNODE pub_keyblock)
                                     sign_mk_attrib, &attrib, NULL);
            if (rc)
              {
+                write_status_error ("keysig", rc);
                log_error (_("signing failed: %s\n"), gpg_strerror (rc));
                goto leave;
              }
@@ -5079,7 +6190,7 @@ menu_revuid (KBNODE pub_keyblock)
                /* If the trustdb has an entry for this key+uid then the
                   trustdb needs an update. */
                if (!update_trust
-                   && (get_validity (pk, uid) & TRUST_MASK) >=
+                   && (get_validity (ctrl, pk, uid, NULL, 0) & TRUST_MASK) >=
                    TRUST_UNDEFINED)
                  update_trust = 1;
 #endif /*!NO_TRUST_MODELS*/
@@ -5129,6 +6240,7 @@ menu_revkey (KBNODE pub_keyblock)
                           revocation_reason_build_cb, reason, NULL);
   if (rc)
     {
+      write_status_error ("keysig", rc);
       log_error (_("signing failed: %s\n"), gpg_strerror (rc));
       goto scram;
     }
@@ -5190,6 +6302,7 @@ menu_revsubkey (KBNODE pub_keyblock)
                                    NULL);
          if (rc)
            {
+              write_status_error ("keysig", rc);
              log_error (_("signing failed: %s\n"), gpg_strerror (rc));
              release_revocation_reason_info (reason);
              return changed;
@@ -5239,7 +6352,7 @@ enable_disable_key (KBNODE keyblock, int disable)
 
 
 static void
-menu_showphoto (KBNODE keyblock)
+menu_showphoto (ctrl_t ctrl, kbnode_t keyblock)
 {
   KBNODE node;
   int select_all = !count_selected_uids (keyblock);
@@ -5276,7 +6389,7 @@ menu_showphoto (KBNODE keyblock)
                                    "key %s (uid %d)\n"),
                                  image_type_to_string (type, 1),
                                  (ulong) size, keystr_from_pk (pk), count);
-                     show_photos (&uid->attribs[i], 1, pk, uid);
+                     show_photos (ctrl, &uid->attribs[i], 1, pk, uid);
                    }
                }
            }