Fix for bug 537
[gnupg.git] / g10 / keyedit.c
index 0211a8b..71ad9f0 100644 (file)
@@ -1,6 +1,6 @@
 /* keyedit.c - keyedit stuff
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
- *               2004 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -16,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include <errno.h>
 #include <assert.h>
 #include <ctype.h>
+#ifdef HAVE_LIBREADLINE
+#define GNUPG_LIBREADLINE_H_INCLUDED
+#include <readline/readline.h>
+#endif
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
 #include "errors.h"
 #include "iobuf.h"
 #include "keydb.h"
-#include "memory.h"
 #include "photoid.h"
 #include "util.h"
 #include "main.h"
 #include "keyserver-internal.h"
 
 static void show_prefs( PKT_user_id *uid, PKT_signature *selfsig, int verbose);
+static void show_names(KBNODE keyblock,PKT_public_key *pk,
+                      unsigned int flag,int with_prefs);
 static void show_key_with_all_names( KBNODE keyblock, int only_marked,
            int with_revoker, int with_fpr, int with_subkeys, int with_prefs );
 static void show_key_and_fingerprint( KBNODE keyblock );
-static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock, int photo );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock,
+                       int photo, const char *photo_name );
 static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
-static int  menu_delsig( 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, KBNODE sec_keyblock );
 static int menu_addrevoker( KBNODE pub_keyblock,
                            KBNODE sec_keyblock, int sensitive );
 static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock);
 static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int menu_set_keyserver_url (const char *url,
                                   KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_notation(const char *string,
+                            KBNODE pub_keyblock,KBNODE sec_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 count_uids( KBNODE keyblock );
 static int count_uids_with_flag( KBNODE keyblock, unsigned flag );
@@ -69,6 +82,7 @@ static int count_selected_keys( KBNODE keyblock );
 static int menu_revsig( KBNODE keyblock );
 static int menu_revuid( KBNODE keyblock, KBNODE sec_keyblock );
 static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
 static int enable_disable_key( KBNODE keyblock, int disable );
 static void menu_showphoto( KBNODE keyblock );
 
@@ -259,7 +273,7 @@ print_and_check_one_sig( KBNODE keyblock, KBNODE node,
            char *p = get_user_id( sig->keyid, &n );
            tty_print_utf8_string2(p, n, opt.screen_columns-keystrlen()-26-
                               ((opt.list_options&LIST_SHOW_SIG_EXPIRE)?11:0));
-           m_free(p);
+           xfree(p);
          }
        tty_printf("\n");
 
@@ -417,7 +431,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
        *trust_value=60;
       else if(p[0]=='2' && !p[1])
        *trust_value=120;
-      m_free(p);
+      xfree(p);
     }
 
   tty_printf("\n");
@@ -434,7 +448,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       trim_spaces(p);
       cpr_kill_prompt();
       *trust_depth=atoi(p);
-      m_free(p);
+      xfree(p);
     }
 
   tty_printf("\n");
@@ -453,7 +467,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       char *q=p;
       int regexplen=100,ind;
 
-      *regexp=m_alloc(regexplen);
+      *regexp=xmalloc(regexplen);
 
       /* Now mangle the domain the user entered into a regexp.  To do
         this, \-escape everything that isn't alphanumeric, and attach
@@ -473,7 +487,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
          if((regexplen-ind)<3)
            {
              regexplen+=100;
-             *regexp=m_realloc(*regexp,regexplen);
+             *regexp=xrealloc(*regexp,regexplen);
            }
 
          q++;
@@ -483,7 +497,7 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
       strcat(*regexp,">$");
     }
 
-  m_free(p);
+  xfree(p);
   tty_printf("\n");
 }
 
@@ -493,8 +507,8 @@ trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
  * if some user_ids are marked those will be signed.
  */
 static int
-sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
-          int local, int nonrevocable, int trust )
+sign_uids( KBNODE keyblock, strlist_t locusr, int *ret_modified,
+          int local, int nonrevocable, int trust, int interactive )
 {
     int rc = 0;
     SK_LIST sk_list = NULL;
@@ -502,7 +516,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
     PKT_secret_key *sk = NULL;
     KBNODE node, uidnode;
     PKT_public_key *primary_pk=NULL;
-    int select_all = !count_selected_uids(keyblock);
+    int select_all = !count_selected_uids(keyblock) || interactive;
     int all_v3=1;
 
     /* Are there any non-v3 sigs on this key already? */
@@ -521,8 +535,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
      * be one which is capable of signing keys.  I can't see a reason
      * 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, 0, PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT);
+     * marked as certification capable will be used. */
+    rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_CERT);
     if( rc )
        goto leave;
 
@@ -535,7 +549,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
        byte trust_depth=0,trust_value=0;
 
        if(local || nonrevocable || trust ||
-          opt.cert_policy_url || opt.cert_notation_data)
+          opt.cert_policy_url || opt.cert_notations)
          force_v4=1;
 
        /* we have to use a copy of the sk, because make_keysig_packet
@@ -571,10 +585,12 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                    force_v4=0;
                  }
            }
-           else if( node->pkt->pkttype == PKT_USER_ID ) {
+           else if( node->pkt->pkttype == PKT_USER_ID )
+             {
                uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
                if(uidnode)
                  {
+                   int yesreally=0;
                    char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
                                              uidnode->pkt->pkt.user_id->len,
                                              0);
@@ -597,6 +613,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -623,6 +641,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -648,6 +668,8 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                uidnode->flag &= ~NODFLG_MARK_A;
                                uidnode=NULL;
                              }
+                           else if(interactive)
+                             yesreally=1;
                          }
                        else
                          {
@@ -657,9 +679,20 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                          }
                      }
 
-                   m_free(user);
+                   if(uidnode && interactive && !yesreally)
+                     {
+                       tty_printf(_("User ID \"%s\" is signable.  "),user);
+                       if(!cpr_get_answer_is_yes("sign_uid.sign_okay",
+                                                 _("Sign it? (y/N) ")))
+                         {
+                           uidnode->flag &= ~NODFLG_MARK_A;
+                           uidnode=NULL;
+                         }
+                     }
+
+                   xfree(user);
                  }
-           }
+             }
            else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
                && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
                if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
@@ -687,7 +720,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                            {
                              force_v4=1;
                              node->flag|=NODFLG_DELSIG;
-                             m_free(user);
+                             xfree(user);
                              continue;
                            }
                      }
@@ -711,7 +744,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                in place. */
 
                            node->flag|=NODFLG_DELSIG;
-                           m_free(user);
+                           xfree(user);
                            continue;
                          }
                      }
@@ -736,7 +769,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                                in place. */
 
                            node->flag|=NODFLG_DELSIG;
-                           m_free(user);
+                           xfree(user);
                            continue;
                          }
                      }
@@ -758,7 +791,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                      {
                        /* Don't delete the old sig here since this is
                           an --expert thing. */
-                       m_free(user);
+                       xfree(user);
                        continue;
                      }
 
@@ -767,7 +800,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                     write_status_text (STATUS_ALREADY_SIGNED, buf);
                    uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
 
-                   m_free(user);
+                   xfree(user);
                }
            }
        }
@@ -808,35 +841,42 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
              }
            else
              {
-               char *answer;
-
                tty_printf(_("This key is due to expire on %s.\n"),
                           expirestr_from_pk(primary_pk));
 
-               answer=cpr_get("sign_uid.expire",
-                              _("Do you want your signature to "
-                                "expire at the same time? (Y/n) "));
-               if(answer_is_yes_no_default(answer,1))
+               if(opt.ask_cert_expire)
                  {
-                   /* This fixes the signature timestamp we're going
-                      to make as now.  This is so the expiration date
-                      is exactly correct, and not a few seconds off
-                      (due to the time it takes to answer the
-                      questions, enter the passphrase, etc). */
-                   timestamp=now;
-                   duration=primary_pk->expiredate-now;
-                   force_v4=1;
-                 }
+                   char *answer=cpr_get("sign_uid.expire",
+                                        _("Do you want your signature to "
+                                          "expire at the same time? (Y/n) "));
+                   if(answer_is_yes_no_default(answer,1))
+                     {
+                       /* This fixes the signature timestamp we're
+                          going to make as now.  This is so the
+                          expiration date is exactly correct, and not
+                          a few seconds off (due to the time it takes
+                          to answer the questions, enter the
+                          passphrase, etc). */
+                       timestamp=now;
+                       duration=primary_pk->expiredate-now;
+                       force_v4=1;
+                     }
 
-               cpr_kill_prompt();
-               m_free(answer);
+                   cpr_kill_prompt();
+                   xfree(answer);
+                 }
              }
          }
 
        /* Only ask for duration if we haven't already set it to match
            the expiration of the pk */
-       if(opt.ask_cert_expire && !duration && !selfsig)
-         duration=ask_expire_interval(1);
+       if(!duration && !selfsig)
+         {
+           if(opt.ask_cert_expire)
+             duration=ask_expire_interval(1,opt.def_cert_expire);
+           else
+             duration=parse_expire_string(opt.def_cert_expire);
+         }
 
        if(duration)
          force_v4=1;
@@ -892,7 +932,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                while(class==0)
                  {
                    answer = cpr_get("sign_uid.class",_("Your selection? "
-                                       "(enter '?' for more information): "));
+                                       "(enter `?' for more information): "));
                    if(answer[0]=='\0')
                      class=0x10+opt.def_cert_level; /* Default */
                    else if(ascii_strcasecmp(answer,"0")==0)
@@ -906,7 +946,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                    else
                      tty_printf(_("Invalid selection.\n"));
 
-                   m_free(answer);
+                   xfree(answer);
                  }
              }
 
@@ -917,7 +957,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
        p=get_user_id_native(sk_keyid);
        tty_printf(_("Are you sure that you want to sign this key with your\n"
                     "key \"%s\" (%s)\n"),p,keystr_from_sk(sk));
-       m_free(p);
+       xfree(p);
 
        if(selfsig)
          {
@@ -1032,7 +1072,7 @@ sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
                *ret_modified = 1; /* we changed the keyblock */
                update_trust = 1;
 
-               pkt = m_alloc_clear( sizeof *pkt );
+               pkt = xmalloc_clear( sizeof *pkt );
                pkt->pkttype = PKT_SIGNATURE;
                pkt->pkt.signature = sig;
                insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
@@ -1068,6 +1108,7 @@ change_passphrase( KBNODE keyblock )
     PKT_secret_key *sk;
     char *passphrase = NULL;
     int no_primary_secrets = 0;
+    int any;
 
     node = find_kbnode( keyblock, PKT_SECRET_KEY );
     if( !node ) {
@@ -1076,6 +1117,25 @@ change_passphrase( KBNODE keyblock )
     }
     sk = node->pkt->pkt.secret_key;
 
+    for (any = 0, node=keyblock; node; node = node->next) {
+       if (node->pkt->pkttype == PKT_SECRET_KEY 
+            || node->pkt->pkttype == PKT_SECRET_SUBKEY) {
+           PKT_secret_key *tmpsk = node->pkt->pkt.secret_key;
+            if (!(tmpsk->is_protected
+                  && (tmpsk->protect.s2k.mode == 1001 
+                      || tmpsk->protect.s2k.mode == 1002))) {
+                any = 1;
+                break;
+            }
+        }
+    }
+    if (!any) {
+        tty_printf (_("Key has only stub or on-card key items - "
+                      "no passphrase to change.\n"));
+        goto leave;
+    }
+        
+    /* See how to handle this key.  */
     switch( is_secret_key_protected( sk ) ) {
       case -1:
        rc = G10ERR_PUBKEY_ALGO;
@@ -1088,6 +1148,10 @@ change_passphrase( KBNODE keyblock )
            tty_printf(_("Secret parts of primary key are not available.\n"));
            no_primary_secrets = 1;
        }
+       else if( sk->protect.s2k.mode == 1002 ) {
+           tty_printf(_("Secret parts of primary key are stored on-card.\n"));
+           no_primary_secrets = 1;
+       }
        else {
            tty_printf(_("Key is protected.\n"));
            rc = check_secret_key( sk, 0 );
@@ -1097,14 +1161,18 @@ change_passphrase( KBNODE keyblock )
        break;
     }
 
-    /* unprotect all subkeys (use the supplied passphrase or ask)*/
+    /* Unprotect all subkeys (use the supplied passphrase or ask)*/
     for(node=keyblock; !rc && node; node = node->next ) {
        if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
            PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-           set_next_passphrase( passphrase );
-           rc = check_secret_key( subsk, 0 );
-           if( !rc && !passphrase )
-               passphrase = get_last_passphrase();
+            if ( !(subsk->is_protected
+                   && (subsk->protect.s2k.mode == 1001 
+                       || subsk->protect.s2k.mode == 1002))) {
+                set_next_passphrase( passphrase );
+                rc = check_secret_key( subsk, 0 );
+                if( !rc && !passphrase )
+                    passphrase = get_last_passphrase();
+            }
        }
     }
 
@@ -1112,7 +1180,7 @@ change_passphrase( KBNODE keyblock )
        tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc));
     else {
        DEK *dek = NULL;
-       STRING2KEY *s2k = m_alloc_secure( sizeof *s2k );
+       STRING2KEY *s2k = xmalloc_secure( sizeof *s2k );
         const char *errtext = NULL;
 
        tty_printf(_("Enter the new passphrase for this secret key.\n\n") );
@@ -1148,24 +1216,29 @@ change_passphrase( KBNODE keyblock )
                for(node=keyblock; !rc && node; node = node->next ) {
                    if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
                        PKT_secret_key *subsk = node->pkt->pkt.secret_key;
-                       subsk->protect.algo = dek->algo;
-                       subsk->protect.s2k = *s2k;
-                       rc = protect_secret_key( subsk, dek );
+                        if ( !(subsk->is_protected
+                               && (subsk->protect.s2k.mode == 1001 
+                                   || subsk->protect.s2k.mode == 1002))) {
+                            subsk->protect.algo = dek->algo;
+                            subsk->protect.s2k = *s2k;
+                            rc = protect_secret_key( subsk, dek );
+                        }
                    }
                }
                if( rc )
-                   log_error("protect_secret_key failed: %s\n", g10_errstr(rc) );
+                   log_error("protect_secret_key failed: %s\n",
+                              g10_errstr(rc) );
                else
                    changed++;
                break;
            }
        }
-       m_free(s2k);
-       m_free(dek);
+       xfree(s2k);
+       xfree(dek);
     }
 
   leave:
-    m_free( passphrase );
+    xfree( passphrase );
     set_next_passphrase( NULL );
     return changed && !rc;
 }
@@ -1214,6 +1287,36 @@ fix_keyblock( KBNODE keyblock )
     return fixed;
 }
 
+static int
+parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig)
+{
+  const char *p=str;
+
+  while(*p)
+    {
+      if(ascii_strncasecmp(p,"l",1)==0)
+       {
+         *localsig=1;
+         p++;
+       }
+      else if(ascii_strncasecmp(p,"nr",2)==0)
+       {
+         *nonrevokesig=1;
+         p+=2;
+       }
+      else if(ascii_strncasecmp(p,"t",1)==0)
+       {
+         *trustsig=1;
+         p++;
+       }
+      else
+       return 0;
+    }
+
+  return 1;
+}
+
+\f
 /****************
  * Menu driven key editor.  If seckey_check is true, then a secret key
  * that matches username will be looked for.  If it is false, not all
@@ -1222,77 +1325,185 @@ fix_keyblock( KBNODE keyblock )
  * Note: to keep track of some selection we use node->mark MARKBIT_xxxx.
  */
 
-void
-keyedit_menu( const char *username, STRLIST locusr,
-             STRLIST commands, int quiet, int seckey_check )
+/* Need an SK for this command */
+#define KEYEDIT_NEED_SK 1
+/* Cannot be viewing the SK for this command */
+#define KEYEDIT_NOT_SK  2
+/* Must be viewing the SK for this command */
+#define KEYEDIT_ONLY_SK 4
+/* Match the tail of the string */
+#define KEYEDIT_TAIL_MATCH 8
+
+enum cmdids
+  {
+    cmdNONE = 0,
+    cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+    cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
+    cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
+    cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
+    cmdEXPIRE, cmdBACKSIGN, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF,
+    cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
+    cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN,
+    cmdMINIMIZE, cmdNOP
+  };
+
+static struct
 {
-    enum cmdids { cmdNONE = 0,
-          cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
-           cmdTSIGN, cmdLSIGN, cmdNRSIGN, cmdNRLSIGN, cmdREVSIG, cmdREVKEY,
-          cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID,
-          cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER,
-          cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE,
-          cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF, cmdSETPREF, cmdUPDPREF,
-          cmdPREFKS, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST,
-           cmdADDCARDKEY, cmdKEYTOCARD,
-   cmdNOP };
-    static struct { const char *name;
-                   enum cmdids id;
-                   int need_sk;
-                    int not_with_sk;  /* but 2 == must use SK */
-                   const char *desc;
-                 } cmds[] = { 
-       { N_("quit")    , cmdQUIT      , 0,0, N_("quit this menu") },
-       { N_("q")       , cmdQUIT      , 0,0, NULL   },
-       { N_("save")    , cmdSAVE      , 0,0, N_("save and quit") },
-       { N_("help")    , cmdHELP      , 0,0, N_("show this help") },
-       {    "?"        , cmdHELP      , 0,0, NULL   },
-       { N_("fpr")     , cmdFPR       , 0,0, N_("show fingerprint") },
-       { N_("list")    , cmdLIST      , 0,0, N_("list key and user IDs") },
-       { N_("l")       , cmdLIST      , 0,0, NULL   },
-       { N_("uid")     , cmdSELUID    , 0,0, N_("select user ID N") },
-       { N_("key")     , cmdSELKEY    , 0,0, N_("select secondary key N") },
-       { N_("check")   , cmdCHECK     , 0,0, N_("list signatures") },
-       { N_("c")       , cmdCHECK     , 0,0, NULL },
-       { N_("sign")    , cmdSIGN      , 0,1, N_("sign the key") },
-       { N_("s")       , cmdSIGN      , 0,1, NULL },
-       { N_("tsign")   , cmdTSIGN     , 0,1, N_("make a trust signature")},
-       { N_("lsign")   , cmdLSIGN     , 0,1, N_("sign the key locally") },
-       { N_("nrsign")  , cmdNRSIGN    , 0,1, N_("sign the key non-revocably") },
-       { N_("nrlsign") , cmdNRLSIGN   , 0,1, N_("sign the key locally and non-revocably") },
-       { N_("debug")   , cmdDEBUG     , 0,0, NULL },
-       { N_("adduid")  , cmdADDUID    , 1,1, N_("add a user ID") },
-       { N_("addphoto"), cmdADDPHOTO  , 1,1, N_("add a photo ID") },
-       { N_("deluid")  , cmdDELUID    , 0,1, N_("delete user ID") },
-       /* delphoto is really deluid in disguise */
-       { N_("delphoto"), cmdDELUID    , 0,1, NULL },
-       { N_("addkey")  , cmdADDKEY    , 1,1, N_("add a secondary key") },
+  const char *name;
+  enum cmdids id;
+  int flags;
+  const char *desc;
+} cmds[] =
+  { 
+    { "quit"    , cmdQUIT      , 0, N_("quit this menu") },
+    { "q"       , cmdQUIT      , 0, NULL   },
+    { "save"    , cmdSAVE      , 0, N_("save and quit") },
+    { "help"    , cmdHELP      , 0, N_("show this help") },
+    { "?"       , cmdHELP      , 0, NULL   },
+    { "fpr"     , cmdFPR       , 0, N_("show key fingerprint") },
+    { "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 },
+    { "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,
+      N_("sign selected user IDs [* see below for related commands]") },
+    { "s"       , cmdSIGN      , KEYEDIT_NOT_SK, NULL },
+    /* "lsign" and friends will never match since "sign" comes first
+       and it is a tail match.  They are just here so they show up in
+       the help menu. */
+    { "lsign"   , cmdNOP       , 0, N_("sign selected user IDs locally") },
+    { "tsign"   , cmdNOP       , 0,
+      N_("sign selected user IDs with a trust signature") },
+    { "nrsign"  , cmdNOP       , 0,
+      N_("sign selected user IDs with a non-revocable signature") },
+
+    { "debug"   , cmdDEBUG     , 0, NULL },
+    { "adduid"  , cmdADDUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a user ID") },
+    { "addphoto", cmdADDPHOTO  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a photo ID") },
+    { "deluid"  , cmdDELUID    , KEYEDIT_NOT_SK,
+      N_("delete selected user IDs") },
+    /* delphoto is really deluid in disguise */
+    { "delphoto", cmdDELUID    , KEYEDIT_NOT_SK, NULL },
+
+    { "addkey"  , cmdADDKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a subkey") },
+
 #ifdef ENABLE_CARD_SUPPORT
-       { N_("addcardkey"), cmdADDCARDKEY , 1,1, N_("add a key to a smartcard") },
-       { N_("keytocard"), cmdKEYTOCARD , 1,2, N_("move a key to a smartcard")},
+    { "addcardkey", cmdADDCARDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a key to a smartcard") },
+    { "keytocard", cmdKEYTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, 
+      N_("move a key to a smartcard")},
+    { "bkuptocard", cmdBKUPTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK, 
+      N_("move a backup key to a smartcard")},
 #endif /*ENABLE_CARD_SUPPORT*/
-       { N_("delkey")  , cmdDELKEY    , 0,1, N_("delete a secondary key") },
-       { N_("addrevoker"),cmdADDREVOKER,1,1, N_("add a revocation key") },
-       { N_("delsig")  , cmdDELSIG    , 0,1, N_("delete signatures") },
-       { N_("expire")  , cmdEXPIRE    , 1,1, N_("change the expire date") },
-        { N_("primary") , cmdPRIMARY   , 1,1, N_("flag user ID as primary")},
-       { N_("toggle")  , cmdTOGGLE    , 1,0, N_("toggle between secret "
-                                                "and public key listing") },
-       { N_("t"     )  , cmdTOGGLE    , 1,0, NULL },
-       { N_("pref")    , cmdPREF      , 0,1, N_("list preferences (expert)")},
-       { N_("showpref"), cmdSHOWPREF  , 0,1, N_("list preferences (verbose)") },
-       { N_("setpref") , cmdSETPREF   , 1,1, N_("set preference list") },
-       { N_("updpref") , cmdUPDPREF   , 1,1, N_("updated preferences") },
-       { N_("keyserver"),cmdPREFKS    , 1,1, N_("set preferred keyserver URL")},
-       { N_("passwd")  , cmdPASSWD    , 1,1, N_("change the passphrase") },
-       { N_("trust")   , cmdTRUST     , 0,1, N_("change the ownertrust") },
-       { N_("revsig")  , cmdREVSIG    , 0,1, N_("revoke signatures") },
-       { N_("revuid")  , cmdREVUID    , 1,1, N_("revoke a user ID") },
-       { N_("revkey")  , cmdREVKEY    , 1,1, N_("revoke a secondary key") },
-       { N_("disable") , cmdDISABLEKEY, 0,1, N_("disable a key") },
-       { N_("enable")  , cmdENABLEKEY , 0,1, N_("enable a key") },
-       { N_("showphoto"),cmdSHOWPHOTO , 0,0, N_("show photo ID") },
-       { NULL, cmdNONE, 0, 0, NULL } };
+
+    { "delkey"  , cmdDELKEY    , KEYEDIT_NOT_SK,
+      N_("delete selected subkeys") },
+    { "addrevoker",cmdADDREVOKER,KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("add a revocation key") },
+    { "delsig"  , cmdDELSIG    , KEYEDIT_NOT_SK,
+      N_("delete signatures from the selected user IDs") },
+    { "expire"  , cmdEXPIRE    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      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") },
+    { "t"       , cmdTOGGLE    , KEYEDIT_NEED_SK, NULL },
+    { "pref"    , cmdPREF      , KEYEDIT_NOT_SK,
+      N_("list preferences (expert)")},
+    { "showpref", cmdSHOWPREF  , KEYEDIT_NOT_SK,
+      N_("list preferences (verbose)") },
+    { "setpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set preference list for the selected user IDs") },
+    /* Alias */
+    { "updpref" , cmdSETPREF   , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "keyserver",cmdPREFKS    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set the preferred keyserver URL for the selected user IDs")},
+    { "notation", cmdNOTATION  , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("set a notation for the selected user IDs")},
+    { "passwd"  , cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("change the passphrase") },
+    /* Alias */
+    { "password", cmdPASSWD    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "trust"   , cmdTRUST     , KEYEDIT_NOT_SK, N_("change the ownertrust") },
+    { "revsig"  , cmdREVSIG    , KEYEDIT_NOT_SK,
+      N_("revoke signatures on the selected user IDs") },
+    { "revuid"  , cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("revoke selected user IDs") },
+    /* Alias */
+    { "revphoto", cmdREVUID    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+    { "revkey"  , cmdREVKEY    , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+      N_("revoke key or selected subkeys") },
+    { "enable"  , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") },
+    { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") },
+    { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") },
+    { "clean",    cmdCLEAN     , KEYEDIT_NOT_SK,
+      N_("compact unusable user IDs and remove unusable signatures from key")},
+    { "minimize", cmdMINIMIZE  , KEYEDIT_NOT_SK,
+      N_("compact unusable user IDs and remove all signatures from key") },
+    { NULL, cmdNONE, 0, NULL }
+  };
+
+#ifdef HAVE_LIBREADLINE
+
+/* These two functions are used by readline for command completion. */
+
+static char *
+command_generator(const char *text,int state)
+{
+  static int list_index,len;
+  const char *name;
+
+  /* If this is a new word to complete, initialize now.  This includes
+     saving the length of TEXT for efficiency, and initializing the
+     index variable to 0. */
+  if(!state)
+    {
+      list_index=0;
+      len=strlen(text);
+    }
+
+  /* Return the next partial match */
+  while((name=cmds[list_index].name))
+    {
+      /* Only complete commands that have help text */
+      if(cmds[list_index++].desc && strncmp(name,text,len)==0)
+       return strdup(name);
+    }
+
+  return NULL;
+}
+
+static char **
+keyedit_completion(const char *text, int start, int end)
+{
+  /* If we are at the start of a line, we try and command-complete.
+     If not, just do nothing for now. */
+
+  if(start==0)
+    return rl_completion_matches(text,command_generator);
+
+  rl_attempted_completion_over=1;
+
+  return NULL;
+}
+#endif /* HAVE_LIBREADLINE */
+
+
+void
+keyedit_menu( const char *username, strlist_t locusr,
+             strlist_t commands, int quiet, int seckey_check )
+{
     enum cmdids cmd = 0;
     int rc = 0;
     KBNODE keyblock = NULL;
@@ -1309,12 +1520,26 @@ keyedit_menu( const char *username, STRLIST locusr,
 
     if ( opt.command_fd != -1 )
         ;
-    else if( opt.batch && !have_commands  ) {
-       log_error(_("can't do that in batchmode\n"));
+    else if( opt.batch && !have_commands )
+      {
+       log_error(_("can't do this in batch mode\n"));
        goto leave;
-    }
+      }
 
-    /* get the public key */
+#ifdef HAVE_W32_SYSTEM
+    /* Due to Windows peculiarities we need to make sure that the
+       trustdb stale check is done before we open another file
+       (i.e. by searching for a key).  In theory we could make sure
+       that the files are closed after use but the open/close caches
+       inhibits that and flushing the cache right before the stale
+       check is not easy to implement.  Thus we take the easy way out
+       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 ();
+#endif
+
+    /* Get the public key */
     rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd, 1);
     if( rc )
        goto leave;
@@ -1373,6 +1598,7 @@ keyedit_menu( const char *username, STRLIST locusr,
        PKT_public_key *pk=keyblock->pkt->pkt.public_key;
 
        tty_printf("\n");
+
        if( redisplay && !quiet )
          {
            show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 );
@@ -1380,22 +1606,25 @@ keyedit_menu( const char *username, STRLIST locusr,
            redisplay = 0;
          }
        do {
-           m_free(answer);
+           xfree(answer);
            if( have_commands ) {
                if( commands ) {
-                   answer = m_strdup( commands->d );
+                   answer = xstrdup( commands->d );
                    commands = commands->next;
                }
                else if( opt.batch ) {
-                   answer = m_strdup("quit");
+                   answer = xstrdup("quit");
                }
                else
                    have_commands = 0;
            }
-           if( !have_commands ) {
+           if( !have_commands )
+             {
+               tty_enable_completion(keyedit_completion);
                answer = cpr_get_no_help("keyedit.prompt", _("Command> "));
                cpr_kill_prompt();
-           }
+               tty_disable_completion();
+             }
            trim_spaces(answer);
        } while( *answer == '#' );
 
@@ -1418,30 +1647,58 @@ keyedit_menu( const char *username, STRLIST locusr,
                 arg_string = p;
            }
 
-           for(i=0; cmds[i].name; i++ ) {
-               if( !ascii_strcasecmp( answer, cmds[i].name ) )
-                   break;
-           }
-           if( cmds[i].need_sk && !sec_keyblock ) {
+           for(i=0; cmds[i].name; i++ )
+             {
+               if(cmds[i].flags & KEYEDIT_TAIL_MATCH)
+                 {
+                   size_t l=strlen(cmds[i].name);
+                   size_t a=strlen(answer);
+                   if(a>=l)
+                     {
+                       if(ascii_strcasecmp(&answer[a-l],cmds[i].name)==0)
+                         {
+                           answer[a-l]='\0';
+                           break;
+                         }
+                     }
+                 }
+               else if( !ascii_strcasecmp( answer, cmds[i].name ) )
+                 break;
+             }
+           if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock )
+             {
                tty_printf(_("Need the secret key to do this.\n"));
                cmd = cmdNOP;
-           }
-           else if(  (cmds[i].not_with_sk == 1 && sec_keyblock && toggle)
-                    ||(cmds[i].not_with_sk == 2 && sec_keyblock && !toggle)) {
+             }
+           else if(((cmds[i].flags & KEYEDIT_NOT_SK) && sec_keyblock
+                    && toggle)
+                    ||((cmds[i].flags & KEYEDIT_ONLY_SK) && sec_keyblock
+                      && !toggle))
+             {
                tty_printf(_("Please use the command \"toggle\" first.\n"));
                cmd = cmdNOP;
-           }
+             }
            else
-               cmd = cmds[i].id;
+             cmd = cmds[i].id;
        }
-       switch( cmd )  {
+       switch( cmd )
+         {
          case cmdHELP:
-           for(i=0; cmds[i].name; i++ ) {
-             if( cmds[i].need_sk && !sec_keyblock )
-               ; /* skip if we do not have the secret key */
-             else if( cmds[i].desc )
-               tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
-           }
+           for(i=0; cmds[i].name; i++ )
+             {
+               if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock )
+                 ; /* skip if we do not have the secret key */
+               else if( cmds[i].desc )
+                 tty_printf("%-11s %s\n", cmds[i].name, _(cmds[i].desc) );
+             }
+
+           tty_printf("\n");
+           tty_printf(_(
+"* The `sign' command may be prefixed with an `l' for local "
+"signatures (lsign),\n"
+"  a `t' for trust signatures (tsign), an `nr' for non-revocable signatures\n"
+"  (nrsign), or any combination thereof (ltsign, tnrsign, etc.).\n"));
+
            break;
 
          case cmdLIST:
@@ -1453,8 +1710,10 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdSELUID:
-           if( menu_select_uid( cur_keyblock, arg_number ) )
-               redisplay = 1;
+           if(strlen(arg_string)==NAMEHASH_LEN*2)
+             redisplay=menu_select_uid_namehash(cur_keyblock,arg_string);
+           else
+             redisplay=menu_select_uid(cur_keyblock,arg_number);
            break;
 
          case cmdSELKEY:
@@ -1470,43 +1729,53 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdSIGN: /* sign (only the public key) */
-         case cmdLSIGN: /* sign (only the public key) */
-         case cmdNRSIGN: /* sign (only the public key) */
-         case cmdNRLSIGN: /* sign (only the public key) */
-         case cmdTSIGN:
-           if( pk->is_revoked )
-             {
-               tty_printf(_("Key is revoked."));
+           {
+             int localsig=0,nonrevokesig=0,trustsig=0,interactive=0;
 
-               if(opt.expert)
-                 {
-                   tty_printf("  ");
-                   if(!cpr_get_answer_is_yes("keyedit.sign_revoked.okay",
-                                             _("Are you sure you still want "
-                                               "to sign it? (y/N) ")))
+             if( pk->is_revoked )
+               {
+                 tty_printf(_("Key is revoked."));
+
+                 if(opt.expert)
+                   {
+                     tty_printf("  ");
+                     if(!cpr_get_answer_is_yes("keyedit.sign_revoked.okay",
+                                               _("Are you sure you still want"
+                                                 " to sign it? (y/N) ")))
+                       break;
+                   }
+                 else
+                   {
+                     tty_printf(_("  Unable to sign.\n"));
                      break;
-                 }
-               else
-                 {
-                   tty_printf(_("  Unable to sign.\n"));
-                   break;
-                 }
-             }
+                   }
+               }
 
-           if( count_uids(keyblock) > 1 && !count_selected_uids(keyblock) )
-             {
-               if( !cpr_get_answer_is_yes("keyedit.sign_all.okay",
-                                       _("Really sign all user IDs? (y/N) ")))
-                 {
-                   tty_printf(_("Hint: Select the user IDs to sign\n"));
-                   break;
-                 }
-             }
+             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;
+                    }
+
+                }
+             /* What sort of signing are we doing? */
+             if(!parse_sign_type(answer,&localsig,&nonrevokesig,&trustsig))
+               {
+                 tty_printf(_("Unknown signature type `%s'\n"),answer);
+                 break;
+               }
 
-           sign_uids( keyblock, locusr, &modified,
-                      (cmd == cmdLSIGN) || (cmd == cmdNRLSIGN),
-                      (cmd == cmdNRSIGN) || (cmd==cmdNRLSIGN),
-                      (cmd == cmdTSIGN));
+             sign_uids(keyblock, locusr, &modified,
+                       localsig, nonrevokesig, trustsig, interactive);
+           }
            break;
 
          case cmdDEBUG:
@@ -1531,7 +1800,7 @@ keyedit_menu( const char *username, STRLIST locusr,
            /* fall through */
 
          case cmdADDUID:
-           if( menu_adduid( keyblock, sec_keyblock, photo ) )
+           if( menu_adduid( keyblock, sec_keyblock, photo, arg_string ) )
              {
                update_trust = 1;
                redisplay = 1;
@@ -1626,6 +1895,69 @@ keyedit_menu( const char *username, STRLIST locusr,
              }
          }
           break;
+
+        case cmdBKUPTOCARD:
+         {
+            /* 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;
+            PACKET *pkt;
+            IOBUF a;
+
+            fname = arg_string;
+            if (!*fname)
+              {
+                tty_printf (_("Command expects a filename argument\n"));
+                break;
+              }
+
+            /* Open that file.  */
+            a = iobuf_open (fname);
+            if (a && is_secured_file (iobuf_get_fd (a)))
+              {
+                iobuf_close (a);
+                a = NULL;
+                errno = EPERM;
+              }
+            if (!a)
+              {
+               tty_printf (_("Can't open `%s': %s\n"),
+                            fname, strerror(errno));
+                break;
+              }
+            
+            /* Parse and check that file.  */
+            pkt = xmalloc (sizeof *pkt);
+            init_packet (pkt);
+            rc = parse_packet (a, pkt);
+            iobuf_close (a);
+            iobuf_ioctl (NULL, 2, 0, (char*)fname); /* (invalidate cache).  */
+            if (!rc 
+                && pkt->pkttype != PKT_SECRET_KEY 
+                && pkt->pkttype != PKT_SECRET_SUBKEY)
+              rc = G10ERR_NO_SECKEY;
+            if (rc)
+              {
+                tty_printf(_("Error reading backup key from `%s': %s\n"),
+                           fname, g10_errstr (rc));
+                free_packet (pkt);
+                xfree (pkt);
+                break;
+              }
+            node = new_kbnode (pkt);
+
+            /* Store it.  */
+            if (card_store_subkey (node, 0))
+              {
+                redisplay = 1;
+                sec_modified = 1;
+              }
+            release_kbnode (node);
+         }
+          break;
+
 #endif /* ENABLE_CARD_SUPPORT */
 
          case cmdDELKEY: {
@@ -1653,7 +1985,7 @@ keyedit_menu( const char *username, STRLIST locusr,
            {
              int sensitive=0;
 
-             if(arg_string && ascii_strcasecmp(arg_string,"sensitive")==0)
+             if(ascii_strcasecmp(arg_string,"sensitive")==0)
                sensitive=1;
              if( menu_addrevoker( keyblock, sec_keyblock, sensitive ) ) {
                redisplay = 1;
@@ -1683,36 +2015,58 @@ keyedit_menu( const char *username, STRLIST locusr,
            }
            break;
 
-         case cmdREVKEY: {
-               int n1;
+         case cmdREVKEY:
+           {
+             int n1;
 
-               if( !(n1=count_selected_keys( keyblock )) )
-                   tty_printf(_("You must select at least one key.\n"));
-               else if( sec_keyblock && !cpr_get_answer_is_yes(
-                           "keyedit.revoke.subkey.okay",
-                      n1 > 1?
-                  _("Do you really want to revoke the selected keys? (y/N) "):
-                  _("Do you really want to revoke this key? (y/N) ")
-                      ))
-                   ;
-               else {
-                   if( menu_revkey( keyblock, sec_keyblock ) ) {
-                       modified = 1;
-                       /*sec_modified = 1;*/
+             if( !(n1=count_selected_keys( keyblock )) )
+               {
+                 if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay",
+                                          _("Do you really want to revoke"
+                                            " the entire key? (y/N) ")))
+                   {
+                     if(menu_revkey(keyblock,sec_keyblock))
+                       modified=1;
+
+                     redisplay=1;
                    }
-                   redisplay = 1;
                }
+             else if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay",
+                                           n1 > 1?
+                                           _("Do you really want to revoke"
+                                             " the selected subkeys? (y/N) "):
+                                           _("Do you really want to revoke"
+                                             " this subkey? (y/N) ")))
+               {
+                 if( menu_revsubkey( keyblock, sec_keyblock ) )
+                   modified = 1;
+
+                 redisplay = 1;
+               }
+
+             if(modified)
+               merge_keys_and_selfsig( keyblock );
            }
            break;
 
          case cmdEXPIRE:
-           if( menu_expire( keyblock, sec_keyblock ) ) {
+           if( menu_expire( keyblock, sec_keyblock ) )
+             {
                merge_keys_and_selfsig( sec_keyblock );
                merge_keys_and_selfsig( keyblock );
                sec_modified = 1;
                modified = 1;
                redisplay = 1;
-           }
+             }
+           break;
+
+         case cmdBACKSIGN:
+           if(menu_backsign(keyblock,sec_keyblock))
+             {
+               sec_modified = 1;
+               modified = 1;
+               redisplay = 1;
+             }
            break;
 
          case cmdPRIMARY:
@@ -1749,38 +2103,48 @@ keyedit_menu( const char *username, STRLIST locusr,
            break;
 
          case cmdPREF:
-           show_key_with_all_names( keyblock, 0, 0, 0, 0, 1 );
+           {
+             int count=count_selected_uids(keyblock);
+             assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+             show_names(keyblock,keyblock->pkt->pkt.public_key,
+                        count?NODFLG_SELUID:0,1);
+           }
            break;
 
          case cmdSHOWPREF:
-           show_key_with_all_names( keyblock, 0, 0, 0, 0, 2 );
+           {
+             int count=count_selected_uids(keyblock);
+             assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+             show_names(keyblock,keyblock->pkt->pkt.public_key,
+                        count?NODFLG_SELUID:0,2);
+           }
            break;
 
           case cmdSETPREF:
-            keygen_set_std_prefs ( !*arg_string? "default" : arg_string, 0);
-            break;
+           {
+             PKT_user_id *tempuid;
 
-         case cmdUPDPREF: 
-            {
-             PKT_user_id *temp=keygen_get_std_prefs();
+             keygen_set_std_prefs(!*arg_string?"default" : arg_string, 0);
+
+             tempuid=keygen_get_std_prefs();
              tty_printf(_("Set preference list to:\n"));
-             show_prefs(temp,NULL,1);
-             m_free(temp);
-            }
-            if (cpr_get_answer_is_yes ("keyedit.updpref.okay",
-                                        count_selected_uids (keyblock)?
-                                       _("Really update the preferences"
-                                        " for the selected user IDs? (y/N) "):
-                                   _("Really update the preferences? (y/N) ")))
-             {
+             show_prefs(tempuid,NULL,1);
+             free_user_id(tempuid);
 
-                if ( menu_set_preferences (keyblock, sec_keyblock) )
-                 {
-                   merge_keys_and_selfsig (keyblock);
-                   modified = 1;
-                   redisplay = 1;
-                 }
-             }
+             if(cpr_get_answer_is_yes("keyedit.setpref.okay",
+                                      count_selected_uids (keyblock)?
+                                      _("Really update the preferences"
+                                        " for the selected user IDs? (y/N) "):
+                                      _("Really update the preferences? (y/N) ")))
+               {
+                 if ( menu_set_preferences (keyblock, sec_keyblock) )
+                   {
+                     merge_keys_and_selfsig (keyblock);
+                     modified = 1;
+                     redisplay = 1;
+                   }
+               }
+           }
            break;
 
          case cmdPREFKS:
@@ -1793,6 +2157,16 @@ keyedit_menu( const char *username, STRLIST locusr,
              }
            break;
 
+         case cmdNOTATION:
+           if( menu_set_notation ( *arg_string?arg_string:NULL,
+                                   keyblock, sec_keyblock ) )
+             {
+               merge_keys_and_selfsig( keyblock );
+               modified = 1;
+               redisplay = 1;
+             }
+           break;
+
          case cmdNOP:
            break;
 
@@ -1811,9 +2185,17 @@ keyedit_menu( const char *username, STRLIST locusr,
            }
            break;
 
-         case cmdSHOWPHOTO:
-           menu_showphoto(keyblock);
-           break;
+         case cmdSHOWPHOTO:
+           menu_showphoto(keyblock);
+           break;
+
+         case cmdCLEAN:
+           redisplay=modified=menu_clean(keyblock,0);
+           break;
+
+         case cmdMINIMIZE:
+           redisplay=modified=menu_clean(keyblock,1);
+           break;
 
          case cmdQUIT:
            if( have_commands )
@@ -1869,9 +2251,38 @@ keyedit_menu( const char *username, STRLIST locusr,
     release_kbnode( keyblock );
     release_kbnode( sec_keyblock );
     keydb_release (kdbhd);
-    m_free(answer);
+    xfree(answer);
 }
 
+static void
+tty_print_notations(int indent,PKT_signature *sig)
+{
+  int first=1;
+  struct notation *notation,*nd;
+
+  if(indent<0)
+    {
+      first=0;
+      indent=-indent;
+    }
+
+  notation=sig_to_notation(sig);
+
+  for(nd=notation;nd;nd=nd->next)
+    {
+      if(!first)
+       tty_printf("%*s",indent,"");
+      else
+       first=0;
+
+      tty_print_utf8_string(nd->name,strlen(nd->name));
+      tty_printf("=");
+      tty_print_utf8_string(nd->value,strlen(nd->value));
+      tty_printf("\n");
+    }
+
+  free_notation(notation);
+}
 
 /****************
  * show preferences of a public keyblock.
@@ -1900,7 +2311,7 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
        tty_printf (_("Cipher: "));
         for(i=any=0; prefs[i].type; i++ ) {
             if( prefs[i].type == PREFTYPE_SYM ) {
-                const char *s = cipher_algo_to_string (prefs[i].value);
+                const char *s = gcry_cipher_algo_name (prefs[i].value);
                 
                 if (any)
                     tty_printf (", ");
@@ -1917,13 +2328,13 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
         if (!des_seen) {
             if (any)
                 tty_printf (", ");
-            tty_printf ("%s",cipher_algo_to_string(CIPHER_ALGO_3DES));
+            tty_printf ("%s", gcry_cipher_algo_name (CIPHER_ALGO_3DES));
         }
         tty_printf ("\n     ");
        tty_printf (_("Digest: "));
         for(i=any=0; prefs[i].type; i++ ) {
             if( prefs[i].type == PREFTYPE_HASH ) {
-                const char *s = digest_algo_to_string (prefs[i].value);
+                const char *s = gcry_md_algo_name (prefs[i].value);
                 
                 if (any)
                     tty_printf (", ");
@@ -1940,7 +2351,7 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
         if (!sha1_seen) {
             if (any)
                 tty_printf (", ");
-            tty_printf ("%s",digest_algo_to_string(DIGEST_ALGO_SHA1));
+            tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1));
         }
         tty_printf ("\n     ");
        tty_printf (_("Compression: "));
@@ -1969,17 +2380,17 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
            }
            tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_NONE));
         }
-       if(uid->mdc_feature || !uid->ks_modify)
+       if(uid->flags.mdc || !uid->flags.ks_modify)
          {
            tty_printf ("\n     ");
            tty_printf (_("Features: "));
            any=0;
-           if(uid->mdc_feature)
+           if(uid->flags.mdc)
              {
                tty_printf ("MDC");
                any=1;
              }
-           if(!uid->ks_modify)
+           if(!uid->flags.ks_modify)
              {
                if(any)
                  tty_printf (", ");
@@ -2002,6 +2413,13 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
                tty_print_utf8_string(pref_ks,pref_ks_len);
                tty_printf("\n");
              }
+
+           if(selfsig->flags.notation)
+             {
+               tty_printf ("     ");
+               tty_printf(_("Notations: "));
+               tty_print_notations(5+strlen(_("Notations: ")),selfsig);
+             }
          }
     }
     else {
@@ -2012,15 +2430,14 @@ show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
                                  prefs[i].type == PREFTYPE_ZIP ? 'Z':'?',
                                  prefs[i].value);
         }
-        if (uid->mdc_feature)
+        if (uid->flags.mdc)
             tty_printf (" [mdc]");
-        if (!uid->ks_modify)
+        if (!uid->flags.ks_modify)
             tty_printf (" [no-ks-modify]");
         tty_printf("\n");
     }
 }
 
-
 /* This is the version of show_key_with_all_names used when
    opt.with_colons is used.  It prints all available data in a easy to
    parse format and does not translate utf8 */
@@ -2077,24 +2494,7 @@ show_key_with_all_names_colon (KBNODE keyblock)
           putchar('\n');
           
           print_fingerprint (pk, NULL, 0);
-
-          /* print the revoker record */
-          if( !pk->revkey && pk->numrevkeys )
-            BUG();
-          else
-            {
-              for (i=0; i < pk->numrevkeys; i++)
-                {
-                  byte *p;
-
-                  printf ("rvk:::%d::::::", pk->revkey[i].algid);
-                  p = pk->revkey[i].fpr;
-                  for (j=0; j < 20; j++, p++ )
-                    printf ("%02X", *p);
-                  printf (":%02x%s:\n", pk->revkey[i].class,
-                          (pk->revkey[i].class&0x40)?"s":"");
-                }
-            }
+         print_revokers(pk);
         }
     }
   
@@ -2154,9 +2554,9 @@ show_key_with_all_names_colon (KBNODE keyblock)
                             prefs[j].type == PREFTYPE_ZIP ? 'Z':'?',
                             prefs[j].value);
                   } 
-                if (uid->mdc_feature)
+                if (uid->flags.mdc)
                   printf (",mdc");
-                if (!uid->ks_modify)
+                if (!uid->flags.ks_modify)
                   printf (",no-ks-modify");
               } 
             putchar (':');
@@ -2178,20 +2578,77 @@ show_key_with_all_names_colon (KBNODE keyblock)
       }
 }
 
-
-/****************
- * Display the key a the user ids, if only_marked is true, do only
- * so for user ids with mark A flag set and dont display the index number
- */
 static void
-show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
-                        int with_fpr, int with_subkeys, int with_prefs )
+show_names(KBNODE keyblock,PKT_public_key *pk,unsigned int flag,int with_prefs)
 {
-    KBNODE node;
-    int i;
-    int do_warn = 0;
-    byte pk_version=0;
-    PKT_public_key *primary=NULL;
+  KBNODE node;
+  int i=0;
+
+  for( node = keyblock; node; node = node->next )
+    {
+      if( node->pkt->pkttype == PKT_USER_ID
+         && !is_deleted_kbnode(node))
+       {
+         PKT_user_id *uid = node->pkt->pkt.user_id;
+         ++i;
+         if(!flag || (flag && (node->flag & flag)))
+           {
+             if(!(flag&NODFLG_MARK_A) && pk)
+               tty_printf("%s ",uid_trust_string_fixed(pk,uid));
+
+             if( flag & NODFLG_MARK_A )
+               tty_printf("     ");
+             else if( node->flag & NODFLG_SELUID )
+               tty_printf("(%d)* ", i);
+             else if( uid->is_primary )
+               tty_printf("(%d). ", i);
+             else
+               tty_printf("(%d)  ", i);
+             tty_print_utf8_string( uid->name, uid->len );
+             tty_printf("\n");
+             if(with_prefs && pk)
+               {
+                 if(pk->version>3 || uid->selfsigversion>3)
+                   {
+                     PKT_signature *selfsig=NULL;
+                     KBNODE signode;
+
+                     for(signode=node->next;
+                         signode && signode->pkt->pkttype==PKT_SIGNATURE;
+                         signode=signode->next)
+                       {
+                         if(signode->pkt->pkt.signature->
+                            flags.chosen_selfsig)
+                           {
+                             selfsig=signode->pkt->pkt.signature;
+                             break;
+                           }
+                       }
+
+                     show_prefs (uid, selfsig, with_prefs == 2);
+                   }
+                 else
+                   tty_printf(_("There are no preferences on a"
+                                " PGP 2.x-style user ID.\n"));
+               }
+           }
+       }
+    }
+}
+
+/****************
+ * Display the key a the user ids, if only_marked is true, do only
+ * so for user ids with mark A flag set and dont display the index number
+ */
+static void
+show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
+                        int with_fpr, int with_subkeys, int with_prefs )
+{
+    KBNODE node;
+    int i;
+    int do_warn = 0;
+    byte pk_version=0;
+    PKT_public_key *primary=NULL;
 
     if (opt.with_colons)
       {
@@ -2202,7 +2659,8 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
     /* the keys */
     for( node = keyblock; node; node = node->next ) {
        if( node->pkt->pkttype == PKT_PUBLIC_KEY
-           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) ) {
+           || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+               && !is_deleted_kbnode(node)) ) {
            PKT_public_key *pk = node->pkt->pkt.public_key;
            const char *otrust="err",*trust="err";
 
@@ -2225,29 +2683,44 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
                primary=pk;
            }
 
-           if(with_revoker) {
+           if(pk->is_revoked)
+             {
+               char *user=get_user_id_string_native(pk->revoked.keyid);
+               const char *algo = gcry_pk_algo_name (pk->revoked.algo);
+               tty_printf(_("This key was revoked on %s by %s key %s\n"),
+                          revokestr_from_pk(pk),algo?algo:"?",user);
+               xfree(user);
+             }
+
+           if(with_revoker)
+             {
                if( !pk->revkey && pk->numrevkeys )
-                   BUG();
+                 BUG();
                else
-                    for(i=0;i<pk->numrevkeys;i++) {
-                        u32 r_keyid[2];
-                        char *user;
-                       const char *algo=
-                         pubkey_algo_to_string(pk->revkey[i].algid);
-
-                        keyid_from_fingerprint(pk->revkey[i].fpr,
-                                               MAX_FINGERPRINT_LEN,r_keyid);
-                        
-                        user=get_user_id_string (r_keyid);
-                        tty_printf (_("This key may be revoked by %s key "),
-                                   algo?algo:"?");
-                        tty_print_utf8_string (user, strlen (user));
-                        if ((pk->revkey[i].class&0x40))
-                          tty_printf (_(" (sensitive)"));
-                        tty_printf ("\n");
-                        m_free(user);
-                      }
-            }
+                 for(i=0;i<pk->numrevkeys;i++)
+                   {
+                     u32 r_keyid[2];
+                     char *user;
+                     const char *algo;
+
+                      algo = gcry_pk_algo_name (pk->revkey[i].algid);
+                     keyid_from_fingerprint(pk->revkey[i].fpr,
+                                            MAX_FINGERPRINT_LEN,r_keyid);
+
+                     user=get_user_id_string_native(r_keyid);
+                     tty_printf(_("This key may be revoked by %s key %s"),
+                                algo?algo:"?",user);
+
+                     if(pk->revkey[i].class&0x40)
+                       {
+                         tty_printf(" ");
+                         tty_printf(_("(sensitive)"));
+                       }
+
+                     tty_printf ("\n");
+                     xfree(user);
+                   }
+             }
 
            keyid_from_pk(pk,NULL);
            tty_printf("%s%c %4u%c/%s  ",
@@ -2273,7 +2746,7 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
              {
                if(opt.trust_model!=TM_ALWAYS)
                  {
-                   tty_printf("%*s",keystrlen()+13,"");
+                   tty_printf("%*s", (int)keystrlen()+13,"");
                    /* Ownertrust is only meaningful for the PGP or
                       classic trust models */
                    if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
@@ -2340,77 +2813,17 @@ show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
               }
          }
     }
-    
-    /* the user ids */
-
-    i = 0;
-    for( node = keyblock; node; node = node->next )
-      {
-       if( node->pkt->pkttype == PKT_USER_ID )
-         {
-           PKT_user_id *uid = node->pkt->pkt.user_id;
-           ++i;
-           if( !only_marked || (only_marked && (node->flag & NODFLG_MARK_A)))
-             {
-               if(!only_marked)
-                 {
-                   if(uid->is_revoked)
-                     tty_printf(_("[%8.8s] "),_("revoked"));
-                   else if(uid->is_expired)
-                     tty_printf(_("[%8.8s] "),_("expired"));
-                   else if(primary)
-                     tty_printf(_("[%8.8s] "),
-                                trust_value_to_string(get_validity(primary,
-                                                                   uid)));
-                 }
-
-               if( only_marked )
-                 tty_printf("     ");
-               else if( node->flag & NODFLG_SELUID )
-                 tty_printf("(%d)* ", i);
-               else if( uid->is_primary )
-                 tty_printf("(%d). ", i);
-               else
-                 tty_printf("(%d)  ", i);
-               tty_print_utf8_string( uid->name, uid->len );
-               tty_printf("\n");
-               if( with_prefs )
-                 {
-                   if(pk_version>3 || uid->selfsigversion>3)
-                     {
-                       PKT_signature *selfsig=NULL;
-                       KBNODE signode;
 
-                       for(signode=node->next;
-                           signode && signode->pkt->pkttype==PKT_SIGNATURE;
-                           signode=signode->next)
-                         {
-                           if(signode->pkt->pkt.signature->
-                              flags.chosen_selfsig)
-                             {
-                               selfsig=signode->pkt->pkt.signature;
-                               break;
-                             }
-                         }
-
-                       show_prefs (uid, selfsig, with_prefs == 2);
-                     }
-                   else
-                     tty_printf(_("There are no preferences on a "
-                                  "PGP 2.x-style user ID.\n"));
-                 }
-             }
-         }
-      }
+    show_names(keyblock,primary,only_marked?NODFLG_MARK_A:0,with_prefs);
 
     if (do_warn)
-        tty_printf (_("Please note that the shown key validity "
-                      "is not necessarily correct\n"
+        tty_printf (_("Please note that the shown key validity"
+                      " is not necessarily correct\n"
                       "unless you restart the program.\n")); 
 }
 
 
-/* Display basic key information.  This fucntion is suitable to show
+/* 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.*/
@@ -2546,7 +2959,8 @@ no_primary_warning(KBNODE keyblock)
  * Return true if there is a new user id
  */
 static int
-menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock,
+            int photo, const char *photo_name)
 {
     PKT_user_id *uid;
     PKT_public_key *pk=NULL;
@@ -2612,7 +3026,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
            }
        }
 
-      uid = generate_photo_id(pk);
+      uid = generate_photo_id(pk,photo_name);
     } else
       uid = generate_user_id();
     if( !uid )
@@ -2628,7 +3042,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
     }
 
     /* insert/append to secret keyblock */
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_USER_ID;
     pkt->pkt.user_id = scopy_user_id(uid);
     node = new_kbnode(pkt);
@@ -2636,7 +3050,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
        insert_kbnode( sec_where, node, 0 );
     else
        add_kbnode( sec_keyblock, node );
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_SIGNATURE;
     pkt->pkt.signature = copy_signature(NULL, sig);
     if( sec_where )
@@ -2644,7 +3058,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
     else
        add_kbnode( sec_keyblock, new_kbnode(pkt) );
     /* insert/append to public keyblock */
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_USER_ID;
     pkt->pkt.user_id = uid;
     node = new_kbnode(pkt);
@@ -2652,7 +3066,7 @@ menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock, int photo)
        insert_kbnode( pub_where, node, 0 );
     else
        add_kbnode( pub_keyblock, node );
-    pkt = m_alloc_clear( sizeof *pkt );
+    pkt = xmalloc_clear( sizeof *pkt );
     pkt->pkttype = PKT_SIGNATURE;
     pkt->pkt.signature = copy_signature(NULL, sig);
     if( pub_where )
@@ -2789,6 +3203,58 @@ menu_delsig( KBNODE pub_keyblock )
     return changed;
 }
 
+static int
+menu_clean(KBNODE keyblock,int self_only)
+{
+  KBNODE uidnode;
+  int modified=0,select_all=!count_selected_uids(keyblock);
+
+  for(uidnode=keyblock->next;
+      uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY;
+      uidnode=uidnode->next)
+    {
+      if(uidnode->pkt->pkttype==PKT_USER_ID
+        && (uidnode->flag&NODFLG_SELUID || select_all))
+       {
+         int uids=0,sigs=0;
+         char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
+                                   uidnode->pkt->pkt.user_id->len,
+                                   0);
+
+         clean_one_uid(keyblock,uidnode,opt.verbose,self_only,&uids,&sigs);
+         if(uids)
+           {
+             const char *reason;
+
+             if(uidnode->pkt->pkt.user_id->is_revoked)
+               reason=_("revoked");
+             else if(uidnode->pkt->pkt.user_id->is_expired)
+               reason=_("expired");
+             else
+               reason=_("invalid");
+
+             tty_printf("User ID \"%s\" compacted: %s\n",user,reason);
+
+             modified=1;
+           }
+         else if(sigs)
+           {
+             tty_printf(sigs==1?
+                        "User ID \"%s\": %d signature removed\n":
+                        "User ID \"%s\": %d signatures removed\n",
+                        user,sigs);
+
+             modified=1;
+           }
+         else
+           tty_printf(_("User ID \"%s\": already clean\n"),user);
+
+         xfree(user);
+       }
+    }
+
+  return modified;
+}
 
 /****************
  * Remove some of the secondary keys
@@ -2899,7 +3365,7 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
       if(revoker_pk)
        free_public_key(revoker_pk);
 
-      revoker_pk=m_alloc_clear(sizeof(*revoker_pk));
+      revoker_pk=xmalloc_clear(sizeof(*revoker_pk));
 
       tty_printf("\n");
 
@@ -2907,22 +3373,24 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
                          _("Enter the user ID of the designated revoker: "));
       if(answer[0]=='\0' || answer[0]=='\004')
        {
-         m_free(answer);
+         xfree(answer);
          goto fail;
        }
 
-      /* Note that I'm requesting SIG here and not CERT.  We're making
-        a certification, but it is okay to be a subkey. */
-      revoker_pk->req_usage=PUBKEY_USAGE_SIG;
+      /* Note that I'm requesting CERT here, which usually implies
+        primary keys only, but some casual testing shows that PGP and
+        GnuPG both can handle a designated revokation from a
+        subkey. */
+      revoker_pk->req_usage=PUBKEY_USAGE_CERT;
       rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL,1);
       if(rc)
        {
          log_error (_("key \"%s\" not found: %s\n"),answer,g10_errstr(rc));
-         m_free(answer);
+         xfree(answer);
          continue;
        }
 
-      m_free(answer);
+      xfree(answer);
 
       fingerprint_from_pk(revoker_pk,revkey.fpr,&fprlen);
       if(fprlen!=20)
@@ -3012,13 +3480,13 @@ menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
   sk=NULL;
 
   /* insert into secret keyblock */
-  pkt = m_alloc_clear( sizeof *pkt );
+  pkt = xmalloc_clear( sizeof *pkt );
   pkt->pkttype = PKT_SIGNATURE;
   pkt->pkt.signature = copy_signature(NULL, sig);
   insert_kbnode( sec_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
 
   /* insert into public keyblock */
-  pkt = m_alloc_clear( sizeof *pkt );
+  pkt = xmalloc_clear( sizeof *pkt );
   pkt->pkttype = PKT_SIGNATURE;
   pkt->pkt.signature = sig;
   insert_kbnode( pub_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
@@ -3056,11 +3524,11 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
 
     n1 = count_selected_keys( pub_keyblock );
     if( n1 > 1 ) {
-       tty_printf(_("Please select at most one secondary key.\n"));
+       tty_printf(_("Please select at most one subkey.\n"));
        return 0;
     }
     else if( n1 )
-       tty_printf(_("Changing expiration time for a secondary key.\n"));
+       tty_printf(_("Changing expiration time for a subkey.\n"));
     else
       {
        tty_printf(_("Changing expiration time for the primary key.\n"));
@@ -3140,18 +3608,18 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
                    return 0;
                }
                /* replace the packet */
-               newpkt = m_alloc_clear( sizeof *newpkt );
+               newpkt = xmalloc_clear( sizeof *newpkt );
                newpkt->pkttype = PKT_SIGNATURE;
                newpkt->pkt.signature = newsig;
                free_packet( node->pkt );
-               m_free( node->pkt );
+               xfree( node->pkt );
                node->pkt = newpkt;
                if( sn ) {
-                   newpkt = m_alloc_clear( sizeof *newpkt );
+                   newpkt = xmalloc_clear( sizeof *newpkt );
                    newpkt->pkttype = PKT_SIGNATURE;
                    newpkt->pkt.signature = copy_signature( NULL, newsig );
                    free_packet( sn->pkt );
-                   m_free( sn->pkt );
+                   xfree( sn->pkt );
                    sn->pkt = newpkt;
                }
                sub_pk = NULL;
@@ -3165,6 +3633,167 @@ menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
 }
 
 static int
+menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+  int rc,modified=0;
+  PKT_public_key *main_pk;
+  PKT_secret_key *main_sk,*sub_sk=NULL;
+  KBNODE node;
+
+  assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+  assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY);
+
+  merge_keys_and_selfsig(pub_keyblock);
+  main_pk=pub_keyblock->pkt->pkt.public_key;
+  main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key);
+  keyid_from_pk(main_pk,NULL);
+
+  for(node=pub_keyblock;node;node=node->next)
+    {
+      PKT_public_key *sub_pk=NULL;
+      KBNODE node2,sig_pk=NULL,sig_sk=NULL;
+      char *passphrase;
+
+      if(sub_sk)
+       {
+         free_secret_key(sub_sk);
+         sub_sk=NULL;
+       }
+
+      /* Find a signing subkey with no backsig */
+      if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+       {
+         if(node->pkt->pkt.public_key->pubkey_usage&PUBKEY_USAGE_SIG)
+           {
+             if(node->pkt->pkt.public_key->backsig)
+               tty_printf(_("signing subkey %s is already cross-certified\n"),
+                          keystr_from_pk(node->pkt->pkt.public_key));
+             else
+               sub_pk=node->pkt->pkt.public_key;
+           }
+         else
+           tty_printf(_("subkey %s does not sign and so does"
+                        " not need to be cross-certified\n"),
+                      keystr_from_pk(node->pkt->pkt.public_key));
+       }
+
+      if(!sub_pk)
+       continue;
+
+      /* Find the selected selfsig on this subkey */
+      for(node2=node->next;
+         node2 && node2->pkt->pkttype==PKT_SIGNATURE;
+         node2=node2->next)
+       if(node2->pkt->pkt.signature->version>=4
+          && node2->pkt->pkt.signature->flags.chosen_selfsig)
+         {
+           sig_pk=node2;
+           break;
+         }
+
+      if(!sig_pk)
+       continue;
+
+      /* Find the secret subkey that matches the public subkey */
+      for(node2=sec_keyblock;node2;node2=node2->next)
+       if(node2->pkt->pkttype==PKT_SECRET_SUBKEY
+          && !cmp_public_secret_key(sub_pk,node2->pkt->pkt.secret_key))
+         {
+           sub_sk=copy_secret_key(NULL,node2->pkt->pkt.secret_key);
+           break;
+         }
+
+      if(!sub_sk)
+       {
+         tty_printf(_("no secret subkey for public subkey %s - ignoring\n"),
+                    keystr_from_pk(sub_pk));
+         continue;
+       }
+
+      /* Now finally find the matching selfsig on the secret subkey.
+        We can't use chosen_selfsig here (it's not set for secret
+        keys), so we just pick the selfsig with the right class.
+        This is what menu_expire does as well. */
+      for(node2=node2->next;
+         node2 && node2->pkt->pkttype!=PKT_SECRET_SUBKEY;
+         node2=node2->next)
+       if(node2->pkt->pkttype==PKT_SIGNATURE
+          && node2->pkt->pkt.signature->version>=4
+          && node2->pkt->pkt.signature->keyid[0]==sig_pk->pkt->pkt.signature->keyid[0]
+          && node2->pkt->pkt.signature->keyid[1]==sig_pk->pkt->pkt.signature->keyid[1]
+          && node2->pkt->pkt.signature->sig_class==sig_pk->pkt->pkt.signature->sig_class)
+         {
+           sig_sk=node2;
+           break;
+         }
+
+      /* Now we can get to work.  We have a main key and secret part,
+        a signing subkey with signature and secret part possibly with
+        signature. */
+
+      passphrase=get_last_passphrase();
+      set_next_passphrase(passphrase);
+      xfree(passphrase);
+
+      rc=make_backsig(sig_pk->pkt->pkt.signature,main_pk,sub_pk,sub_sk);
+      if(rc==0)
+       {
+         PKT_signature *newsig;
+         PACKET *newpkt;
+
+         passphrase=get_last_passphrase();
+         set_next_passphrase(passphrase);
+         xfree(passphrase);
+
+         rc=update_keysig_packet(&newsig,sig_pk->pkt->pkt.signature,main_pk,
+                                 NULL,sub_pk,main_sk,NULL,NULL);
+         if(rc==0)
+           {
+             /* Put the new sig into place on the pubkey */
+             newpkt=xmalloc_clear(sizeof(*newpkt));
+             newpkt->pkttype=PKT_SIGNATURE;
+             newpkt->pkt.signature=newsig;
+             free_packet(sig_pk->pkt);
+             xfree(sig_pk->pkt);
+             sig_pk->pkt=newpkt;
+
+             if(sig_sk)
+               {
+                 /* Put the new sig into place on the seckey */
+                 newpkt=xmalloc_clear(sizeof(*newpkt));
+                 newpkt->pkttype=PKT_SIGNATURE;
+                 newpkt->pkt.signature=copy_signature(NULL,newsig);
+                 free_packet(sig_sk->pkt);
+                 xfree(sig_sk->pkt);
+                 sig_sk->pkt=newpkt;
+               }
+
+             modified=1;
+           }
+         else
+           {
+             log_error("update_keysig_packet failed: %s\n",g10_errstr(rc));
+             break;
+           }
+       }
+      else
+       {
+         log_error("make_backsig failed: %s\n",g10_errstr(rc));
+         break;
+       }
+    }
+
+  set_next_passphrase(NULL);
+
+  free_secret_key(main_sk);
+  if(sub_sk)
+    free_secret_key(sub_sk);
+
+  return modified;
+}
+
+
+static int
 change_primary_uid_cb ( PKT_signature *sig, void *opaque )
 {
     byte buf[1];
@@ -3246,7 +3875,7 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
 
                log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
                         user);
-               m_free(user);
+               xfree(user);
              }
              else {
                /* This is a selfsignature which is to be replaced.
@@ -3290,11 +3919,11 @@ menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
                         return 0;
                     }
                     /* replace the packet */
-                    newpkt = m_alloc_clear( sizeof *newpkt );
+                    newpkt = xmalloc_clear( sizeof *newpkt );
                     newpkt->pkttype = PKT_SIGNATURE;
                     newpkt->pkt.signature = newsig;
                     free_packet( node->pkt );
-                    m_free( node->pkt );
+                    xfree( node->pkt );
                     node->pkt = newpkt;
                     modified = 1;
                }
@@ -3356,7 +3985,7 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
 
                log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
                         user);
-               m_free(user);
+               xfree(user);
              }
              else {
                /* This is a selfsignature which is to be replaced 
@@ -3378,11 +4007,11 @@ menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
                     return 0;
                 }
                 /* replace the packet */
-                newpkt = m_alloc_clear( sizeof *newpkt );
+                newpkt = xmalloc_clear( sizeof *newpkt );
                 newpkt->pkttype = PKT_SIGNATURE;
                 newpkt->pkt.signature = newsig;
                 free_packet( node->pkt );
-                m_free( node->pkt );
+                xfree( node->pkt );
                 node->pkt = newpkt;
                 modified = 1;
              }
@@ -3411,14 +4040,14 @@ menu_set_keyserver_url (const char *url,
   no_primary_warning(pub_keyblock);
 
   if(url)
-    answer=m_strdup(url);
+    answer=xstrdup(url);
   else
     {
       answer=cpr_get_utf8("keyedit.add_keyserver",
                          _("Enter your preferred keyserver URL: "));
       if(answer[0]=='\0' || answer[0]=='\004')
        {
-         m_free(answer);
+         xfree(answer);
          return 0;
        }
     }
@@ -3430,13 +4059,13 @@ menu_set_keyserver_url (const char *url,
       struct keyserver_spec *keyserver=NULL;
       /* Sanity check the format */
       keyserver=parse_keyserver_uri(answer,1,NULL,0);
-      m_free(answer);
+      xfree(answer);
       if(!keyserver)
        {
          log_info(_("could not parse keyserver URL\n"));
          return 0;
        }
-      uri=m_strdup(keyserver->uri);
+      uri=xstrdup(keyserver->uri);
       free_keyserver_spec(keyserver);
     }
 
@@ -3469,7 +4098,8 @@ menu_set_keyserver_url (const char *url,
        {
          PKT_signature *sig = node->pkt->pkt.signature;
          if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
-              && (uid && (sig->sig_class&~3) == 0x10) )
+              && (uid && (sig->sig_class&~3) == 0x10)
+              && sig->flags.chosen_selfsig)
            {
              char *user=utf8_to_native(uid->name,strlen(uid->name),0);
              if( sig->version < 4 )
@@ -3514,25 +4144,238 @@ menu_set_keyserver_url (const char *url,
                      log_error ("update_keysig_packet failed: %s\n",
                                 g10_errstr(rc));
                      free_secret_key( sk );
-                     m_free(uri);
+                     xfree(uri);
                      return 0;
                    }
                  /* replace the packet */
-                 newpkt = m_alloc_clear( sizeof *newpkt );
+                 newpkt = xmalloc_clear( sizeof *newpkt );
                  newpkt->pkttype = PKT_SIGNATURE;
                  newpkt->pkt.signature = newsig;
                  free_packet( node->pkt );
-                 m_free( node->pkt );
+                 xfree( node->pkt );
                  node->pkt = newpkt;
                  modified = 1;
                }
 
-             m_free(user);
+             xfree(user);
+           }
+       }
+    }
+
+  xfree(uri);
+  free_secret_key( sk );
+  return modified;
+}
+
+static int
+menu_set_notation(const char *string,KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+  PKT_secret_key *sk;    /* copy of the main sk */
+  PKT_public_key *main_pk;
+  PKT_user_id *uid;
+  KBNODE node;
+  u32 keyid[2];
+  int selected, select_all;
+  int modified = 0;
+  char *answer;
+  struct notation *notation;
+
+  no_primary_warning(pub_keyblock);
+
+  if(string)
+    answer=xstrdup(string);
+  else
+    {
+      answer=cpr_get_utf8("keyedit.add_notation",
+                         _("Enter the notation: "));
+      if(answer[0]=='\0' || answer[0]=='\004')
+       {
+         xfree(answer);
+         return 0;
+       }
+    }
+
+  if(ascii_strcasecmp(answer,"none")==0
+     || ascii_strcasecmp(answer,"-")==0)
+    notation=NULL; /* delete them all */
+  else
+    {
+      notation=string_to_notation(answer,0);
+      if(!notation)
+       {
+         xfree(answer);
+         return 0;
+       }
+    }
+
+  xfree(answer);
+
+  select_all = !count_selected_uids (pub_keyblock);
+
+  node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+  sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+  /* Now we can actually change the self signature(s) */
+  main_pk = NULL;
+  uid = NULL;
+  selected = 0;
+  for ( node=pub_keyblock; node; node = node->next )
+    {
+      if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+       break; /* ready */
+
+      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_USER_ID )
+       {
+         uid = node->pkt->pkt.user_id;
+         selected = select_all || (node->flag & NODFLG_SELUID);
+       }
+      else if ( main_pk && uid && selected
+               && node->pkt->pkttype == PKT_SIGNATURE )
+       {
+         PKT_signature *sig = node->pkt->pkt.signature;
+         if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+              && (uid && (sig->sig_class&~3) == 0x10)
+              && sig->flags.chosen_selfsig)
+           {
+             char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+             if( sig->version < 4 )
+               log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+                        user);
+             else
+               {
+                 PKT_signature *newsig;
+                 PACKET *newpkt;
+                 int rc,skip=0,addonly=1;
+
+                 if(sig->flags.notation)
+                   {
+                     tty_printf("Current notations for user ID \"%s\":\n",
+                                user);
+                     tty_print_notations(-9,sig);
+                   }
+                 else
+                   {
+                     tty_printf("No notations on user ID \"%s\"\n",user);
+                     if(notation==NULL)
+                       {
+                         /* There are no current notations, so there
+                            is no point in trying to un-set them. */
+                         continue;
+                       }
+                   }
+
+                 if(notation)
+                   {
+                     struct notation *n;
+                     int deleting=0;
+
+                     notation->next=sig_to_notation(sig);
+
+                     for(n=notation->next;n;n=n->next)
+                       if(strcmp(n->name,notation->name)==0)
+                         {
+                           if(notation->value)
+                             {
+                               if(strcmp(n->value,notation->value)==0)
+                                 {
+                                   if(notation->flags.ignore)
+                                     {
+                                       /* Value match with a delete
+                                          flag. */
+                                       n->flags.ignore=1;
+                                       deleting=1;
+                                     }
+                                   else
+                                     {
+                                       /* Adding the same notation
+                                          twice, so don't add it at
+                                          all. */
+                                       skip=1;
+                                       tty_printf("Skipping notation:"
+                                                  " %s=%s\n",
+                                                  notation->name,
+                                                  notation->value);
+                                       break;
+                                     }
+                                 }
+                             }
+                           else
+                             {
+                               /* No value, so it means delete. */
+                               n->flags.ignore=1;
+                               deleting=1;
+                             }
+
+                           if(n->flags.ignore)
+                             {
+                               tty_printf("Removing notation: %s=%s\n",
+                                          n->name,n->value);
+                               addonly=0;
+                             }
+                         }
+
+                     if(!notation->flags.ignore && !skip)
+                       tty_printf("Adding notation: %s=%s\n",
+                                  notation->name,notation->value);
+
+                     /* We tried to delete, but had no matches */
+                     if(notation->flags.ignore && !deleting)
+                       continue;
+                   }
+                 else
+                   {
+                     tty_printf("Removing all notations\n");
+                     addonly=0;
+                   }
+
+                 if(skip
+                    || (!addonly
+                        && !cpr_get_answer_is_yes("keyedit.confirm_notation",
+                                                  _("Proceed? (y/N) "))))
+                   continue;
+
+                 rc = update_keysig_packet (&newsig, sig,
+                                            main_pk, uid, NULL,
+                                            sk,
+                                            keygen_add_notations, notation );
+                 if( rc )
+                   {
+                     log_error ("update_keysig_packet failed: %s\n",
+                                g10_errstr(rc));
+                     free_secret_key( sk );
+                     free_notation(notation);
+                     xfree(user);
+                     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;
+                 modified = 1;
+
+                 if(notation)
+                   {
+                     /* Snip off the notation list from the sig */
+                     free_notation(notation->next);
+                     notation->next=NULL;
+                   }
+
+                 xfree(user);
+               }
            }
        }
     }
 
-  m_free(uri);
+  free_notation(notation);
   free_secret_key( sk );
   return modified;
 }
@@ -3583,6 +4426,45 @@ menu_select_uid( KBNODE keyblock, int idx )
     return 1;
 }
 
+/* Search in the keyblock for a uid that matches namehash */
+static int
+menu_select_uid_namehash( KBNODE keyblock, const char *namehash )
+{
+  byte hash[NAMEHASH_LEN];
+  KBNODE node;
+  int i;
+
+  assert(strlen(namehash)==NAMEHASH_LEN*2);
+
+  for(i=0;i<NAMEHASH_LEN;i++)
+    hash[i]=hextobyte(&namehash[i*2]);
+
+  for(node=keyblock->next;node;node=node->next)
+    {
+      if(node->pkt->pkttype==PKT_USER_ID)
+       {
+         namehash_from_uid(node->pkt->pkt.user_id);
+         if(memcmp(node->pkt->pkt.user_id->namehash,hash,NAMEHASH_LEN)==0)
+           {
+             if(node->flag&NODFLG_SELUID)
+               node->flag &= ~NODFLG_SELUID;
+             else
+               node->flag |= NODFLG_SELUID;
+
+             break;
+           }
+       }
+    }
+
+    if(!node)
+      {
+       tty_printf(_("No user ID with hash %s\n"),namehash);
+       return 0;
+      }
+
+  return 1;
+}
+
 /****************
  * Select secondary keys
  * Returns: True if the selection changed;
@@ -3603,7 +4485,7 @@ menu_select_key( KBNODE keyblock, int idx )
            }
        }
        if( !node ) {
-           tty_printf(_("No secondary key with index %d\n"), idx );
+           tty_printf(_("No subkey with index %d\n"), idx );
            return 0;
        }
     }
@@ -3709,7 +4591,7 @@ static void
 ask_revoke_sig( KBNODE keyblock, KBNODE node )
 {
     int doit=0;
-    char *p;
+    PKT_user_id *uid;
     PKT_signature *sig = node->pkt->pkt.signature;
     KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID );
 
@@ -3718,15 +4600,33 @@ ask_revoke_sig( KBNODE keyblock, KBNODE node )
        return;
     }
 
-    p=utf8_to_native(unode->pkt->pkt.user_id->name,
-                    unode->pkt->pkt.user_id->len,0);
-    tty_printf(_("user ID: \"%s\"\n"),p);
-    m_free(p);
+    uid=unode->pkt->pkt.user_id;
+
+    if(opt.with_colons)
+      {
+       if(uid->attrib_data)
+         printf("uat:::::::::%u %lu",uid->numattribs,uid->attrib_len);
+       else
+         {
+           printf("uid:::::::::");
+           print_string (stdout, uid->name, uid->len, ':');
+         }
 
-    tty_printf(_("signed by your key %s on %s%s%s\n"),
-              keystr(sig->keyid),datestr_from_sig(sig),
-              sig->flags.exportable?"":_(" (non-exportable)"),"");
+       printf("\n");
 
+       print_and_check_one_sig_colon(keyblock,node,NULL,NULL,NULL,NULL,1);
+      }
+    else
+      {
+       char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+                        unode->pkt->pkt.user_id->len,0);
+       tty_printf(_("user ID: \"%s\"\n"),p);
+       xfree(p);
+
+       tty_printf(_("signed by your key %s on %s%s%s\n"),
+                  keystr(sig->keyid),datestr_from_sig(sig),
+                  sig->flags.exportable?"":_(" (non-exportable)"),"");
+      }
     if(sig->flags.expired)
       {
        tty_printf(_("This signature expired on %s.\n"),
@@ -3867,7 +4767,7 @@ menu_revsig( KBNODE keyblock )
        attrib.non_exportable=!node->pkt->pkt.signature->flags.exportable;
 
        node->flag &= ~NODFLG_MARK_A;
-       sk = m_alloc_secure_clear( sizeof *sk );
+       sk = xmalloc_secure_clear( sizeof *sk );
        if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) {
            log_info(_("no secret key\n"));
            continue;
@@ -3891,7 +4791,7 @@ menu_revsig( KBNODE keyblock )
        if(primary_pk->keyid[0]==sig->keyid[0] &&
           primary_pk->keyid[1]==sig->keyid[1])
          unode->pkt->pkt.user_id->is_revoked=1;
-       pkt = m_alloc_clear( sizeof *pkt );
+       pkt = xmalloc_clear( sizeof *pkt );
        pkt->pkttype = PKT_SIGNATURE;
        pkt->pkt.signature = sig;
        insert_kbnode( unode, new_kbnode(pkt), 0 );
@@ -3940,7 +4840,7 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
          {
            char *user=utf8_to_native(uid->name,uid->len,0);
            log_info(_("user ID \"%s\" is already revoked\n"),user);
-           m_free(user);
+           xfree(user);
          }
        else
          {
@@ -3977,7 +4877,7 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
              }
            else
              {
-               pkt = m_alloc_clear( sizeof *pkt );
+               pkt = xmalloc_clear( sizeof *pkt );
                pkt->pkttype = PKT_SIGNATURE;
                pkt->pkt.signature = sig;
                insert_kbnode( node, new_kbnode(pkt), 0 );
@@ -4006,13 +4906,58 @@ menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
 }
 
 /****************
- * Revoke some of the secondary keys.
- * Hmmm: Should we add a revocation to the secret keyring too?
- *      Does its all make sense to duplicate most of the information?
+ * Revoke the whole key.
  */
 static int
 menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
 {
+  PKT_public_key *pk=pub_keyblock->pkt->pkt.public_key;
+  PKT_secret_key *sk;
+  int rc,changed = 0;
+  struct revocation_reason_info *reason;
+  PACKET *pkt;
+  PKT_signature *sig;
+
+  if(pk->is_revoked)
+    {
+      tty_printf(_("Key %s is already revoked.\n"),keystr_from_pk(pk));
+      return 0;
+    }
+
+  reason = ask_revocation_reason( 1, 0, 0 );
+  /* user decided to cancel */
+  if( !reason )
+    return 0;
+
+  sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key );
+  rc = make_keysig_packet( &sig, pk, NULL, NULL, sk,
+                          0x20, 0, opt.force_v4_certs?4:0, 0, 0,
+                          revocation_reason_build_cb, reason );
+  free_secret_key(sk);
+  if( rc )
+    {
+      log_error(_("signing failed: %s\n"), g10_errstr(rc));
+      goto scram;
+    }
+
+  changed = 1; /* we changed the keyblock */
+
+  pkt = xmalloc_clear( sizeof *pkt );
+  pkt->pkttype = PKT_SIGNATURE;
+  pkt->pkt.signature = sig;
+  insert_kbnode( pub_keyblock, new_kbnode(pkt), 0 );
+  commit_kbnode( &pub_keyblock );
+
+  update_trust=1;
+
+ scram:
+  release_revocation_reason_info( reason );
+  return changed;
+}
+
+static int
+menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
     PKT_public_key *mainpk;
     KBNODE node;
     int changed = 0;
@@ -4035,6 +4980,13 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
            PKT_public_key *subpk = node->pkt->pkt.public_key;
            struct sign_attrib attrib;
 
+           if(subpk->is_revoked)
+             {
+               tty_printf(_("Subkey %s is already revoked.\n"),
+                          keystr_from_pk(subpk));
+               continue;
+             }
+
            memset( &attrib, 0, sizeof attrib );
            attrib.reason = reason;
 
@@ -4051,7 +5003,7 @@ menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
            }
            changed = 1; /* we changed the keyblock */
 
-           pkt = m_alloc_clear( sizeof *pkt );
+           pkt = xmalloc_clear( sizeof *pkt );
            pkt->pkttype = PKT_SIGNATURE;
            pkt->pkt.signature = sig;
            insert_kbnode( node, new_kbnode(pkt), 0 );