gpg: Use only OpenPGP public key algo ids and add the EdDSA algo id.
[gnupg.git] / g10 / revoke.c
index e711245..6e47691 100644 (file)
@@ -1,11 +1,12 @@
 /* revoke.c
- *     Copyright (C) 1998 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ *               2004 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <ctype.h>
 
+#include "gpg.h"
 #include "options.h"
 #include "packet.h"
-#include "errors.h"
+#include "status.h"
 #include "keydb.h"
-#include <gcrypt.h>
 #include "util.h"
 #include "main.h"
 #include "ttyio.h"
 #include "status.h"
 #include "i18n.h"
+#include "call-agent.h"
+
+struct revocation_reason_info {
+    int code;
+    char *desc;
+};
 
 
-/****************
- * Generate a revocation certificate for UNAME
- */
 int
-gen_revoke( const char *uname )
+revocation_reason_build_cb( PKT_signature *sig, void *opaque )
 {
-    int rc = 0;
-    armor_filter_context_t afx;
-    compress_filter_context_t zfx;
-    PACKET pkt;
-    PKT_secret_key *sk; /* used as pointer into a kbnode */
-    PKT_public_key *pk = NULL;
-    PKT_signature *sig = NULL;
-    u32 sk_keyid[2];
-    IOBUF out = NULL;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-
-    if( opt.batch ) {
-       log_error(_("sorry, can't do this in batch mode\n"));
-       return G10ERR_GENERAL;
-    }
+    struct revocation_reason_info *reason = opaque;
+    char *ud = NULL;
+    byte *buffer;
+    size_t buflen = 1;
 
+    if(!reason)
+      return 0;
 
-    /* FIXME: ask for the reason of revocation
-       0x00 - No reason specified (key revocations or cert revocations)
-    Does not make sense!
-
-       0x01 - Key is superceded (key revocations)
-       0x02 - Key material has been compromised (key revocations)
-       0x03 - Key is no longer used (key revocations)
-       0x20 - User id information is no longer valid (cert revocations)
+    if( reason->desc ) {
+       ud = native_to_utf8( reason->desc );
+       buflen += strlen(ud);
+    }
+    buffer = xmalloc( buflen );
+    *buffer = reason->code;
+    if( ud ) {
+       memcpy(buffer+1, ud, strlen(ud) );
+       xfree( ud );
+    }
 
-       Following the revocation code is a string of octets which gives
-      information about the reason for revocation in human-readable form
-     */
+    build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
+    xfree( buffer );
+    return 0;
+}
 
-    memset( &afx, 0, sizeof afx);
-    memset( &zfx, 0, sizeof zfx);
-    init_packet( &pkt );
+/* Outputs a minimal pk (as defined by 2440) from a keyblock.  A
+   minimal pk consists of the public key packet and a user ID.  We try
+   and pick a user ID that has a uid signature, and include it if
+   possible. */
+static int
+export_minimal_pk(IOBUF out,KBNODE keyblock,
+                 PKT_signature *revsig,PKT_signature *revkey)
+{
+  KBNODE node;
+  PACKET pkt;
+  PKT_user_id *uid=NULL;
+  PKT_signature *selfsig=NULL;
+  u32 keyid[2];
+  int rc;
+
+  node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
+  if(!node)
+    {
+      log_error("key incomplete\n");
+      return G10ERR_GENERAL;
+    }
 
+  keyid_from_pk(node->pkt->pkt.public_key,keyid);
 
-    /* search the userid */
-    rc = find_secret_keyblock_byname( &kbpos, uname );
-    if( rc ) {
-       log_error(_("secret key for user `%s' not found\n"), uname );
-       goto leave;
+  pkt=*node->pkt;
+  rc=build_packet(out,&pkt);
+  if(rc)
+    {
+      log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+      return rc;
     }
 
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error(_("error reading the certificate: %s\n"), g10_errstr(rc) );
-       goto leave;
-    }
+  init_packet(&pkt);
+  pkt.pkttype=PKT_SIGNATURE;
 
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_SECRET_KEY );
-    if( !node ) { /* maybe better to use log_bug ? */
-       log_error(_("Oops; secret key not found anymore!\n"));
-       rc = G10ERR_GENERAL;
-       goto leave;
+  /* the revocation itself, if any.  2440 likes this to come first. */
+  if(revsig)
+    {
+      pkt.pkt.signature=revsig;
+      rc=build_packet(out,&pkt);
+      if(rc)
+       {
+         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+         return rc;
+       }
     }
 
-    /* fixme: should make a function out of this stuff,
-     * it's used all over the source */
-    sk = node->pkt->pkt.secret_key;
-    keyid_from_sk( sk, sk_keyid );
-    tty_printf("\nsec  %4u%c/%08lX %s   ",
-             nbits_from_sk( sk ),
-             pubkey_letter( sk->pubkey_algo ),
-             sk_keyid[1], datestr_from_sk(sk) );
+  /* If a revkey in a 1F sig is present, include it too */
+  if(revkey)
     {
-       size_t n;
-       char *p = get_user_id( sk_keyid, &n );
-       tty_print_utf8_string( p, n );
-       gcry_free(p);
-       tty_printf("\n");
-    }
-    pk = gcry_xcalloc( 1, sizeof *pk );
-    rc = get_pubkey( pk, sk_keyid );
-    if( rc ) {
-       log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) );
-       goto leave;
-    }
-    if( cmp_public_secret_key( pk, sk ) ) {
-       log_error(_("public key does not match secret key!\n") );
-       rc = G10ERR_GENERAL;
-       goto leave;
+      pkt.pkt.signature=revkey;
+      rc=build_packet(out,&pkt);
+      if(rc)
+       {
+         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+         return rc;
+       }
     }
 
-    tty_printf("\n");
-    if( !cpr_get_answer_is_yes("gen_revoke.okay",
-                       _("Create a revocation certificate for this key? ")) ){
-       rc = 0;
-       goto leave;
+  while(!selfsig)
+    {
+      KBNODE signode;
+
+      node=find_next_kbnode(node,PKT_USER_ID);
+      if(!node)
+       {
+         /* We're out of user IDs - none were self-signed. */
+         if(uid)
+           break;
+         else
+           {
+             log_error(_("key %s has no user IDs\n"),keystr(keyid));
+             return G10ERR_GENERAL;
+           }
+       }
+
+      if(node->pkt->pkt.user_id->attrib_data)
+       continue;
+
+      uid=node->pkt->pkt.user_id;
+      signode=node;
+
+      while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
+       {
+         if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
+            keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
+            IS_UID_SIG(signode->pkt->pkt.signature))
+           {
+             selfsig=signode->pkt->pkt.signature;
+             break;
+           }
+       }
     }
 
-    switch( is_secret_key_protected( sk ) ) {
-      case -1:
-       log_error(_("unknown protection algorithm\n"));
-       rc = G10ERR_PUBKEY_ALGO;
-       break;
-      case 0:
-       tty_printf(_("NOTE: This key is not protected!\n"));
-       break;
-      default:
-       rc = check_secret_key( sk, 0 );
-       break;
+  pkt.pkttype=PKT_USER_ID;
+  pkt.pkt.user_id=uid;
+
+  rc=build_packet(out,&pkt);
+  if(rc)
+    {
+      log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+      return rc;
     }
-    if( rc )
-       goto leave;
 
+  if(selfsig)
+    {
+      pkt.pkttype=PKT_SIGNATURE;
+      pkt.pkt.signature=selfsig;
+
+      rc=build_packet(out,&pkt);
+      if(rc)
+       {
+         log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+         return rc;
+       }
+    }
 
-    if( !opt.armor )
-       tty_printf(_("ASCII armored output forced.\n"));
+  return 0;
+}
 
-    if( (rc = open_outfile( NULL, 0, &out )) )
-       goto leave;
+/****************
+ * Generate a revocation certificate for UNAME via a designated revoker
+ */
+int
+gen_desig_revoke( const char *uname, strlist_t locusr )
+{
+    int rc = 0;
+    armor_filter_context_t *afx;
+    PKT_public_key *pk = NULL;
+    PKT_public_key *pk2 = NULL;
+    PKT_signature *sig = NULL;
+    IOBUF out = NULL;
+    struct revocation_reason_info *reason = NULL;
+    KEYDB_HANDLE kdbhd;
+    KEYDB_SEARCH_DESC desc;
+    KBNODE keyblock=NULL,node;
+    u32 keyid[2];
+    int i,any=0;
+    SK_LIST sk_list=NULL;
+
+    if( opt.batch )
+      {
+       log_error(_("can't do this in batch mode\n"));
+       return G10ERR_GENERAL;
+      }
 
-    afx.what = 1;
-    afx.hdrlines = "Comment: A revocation certificate should follow\n";
-    iobuf_push_filter( out, armor_filter, &afx );
+    afx = new_armor_context ();
 
-    /* create it */
-    rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0, NULL, NULL);
-    if( rc ) {
-       log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
+    kdbhd = keydb_new ();
+    rc = classify_user_id (uname, &desc, 1);
+    if (!rc)
+      rc = keydb_search (kdbhd, &desc, 1, NULL);
+    if (rc) {
+       log_error (_("key \"%s\" not found: %s\n"),uname, g10_errstr (rc));
        goto leave;
     }
-    init_packet( &pkt );
-    pkt.pkttype = PKT_SIGNATURE;
-    pkt.pkt.signature = sig;
 
-    rc = build_packet( out, &pkt );
+    rc = keydb_get_keyblock (kdbhd, &keyblock );
     if( rc ) {
-       log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+       log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
        goto leave;
     }
 
-    /* and issue a usage notice */
-    tty_printf(_("Revocation certificate created.\n\n"
-"Please move it to a medium which you can hide away; if Mallory gets\n"
-"access to this certificate he can use it to make your key unusable.\n"
-"It is smart to print this certificate and store it away, just in case\n"
-"your media become unreadable.  But have some caution:  The print system of\n"
-"your machine might store the data and make it available to others!\n"));
+    /* To parse the revkeys */
+    merge_keys_and_selfsig(keyblock);
 
+    /* get the key from the keyblock */
+    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+    if( !node )
+      BUG ();
+
+    pk=node->pkt->pkt.public_key;
+
+    keyid_from_pk(pk,keyid);
+
+    if(locusr)
+      {
+       rc=build_sk_list(locusr, &sk_list, PUBKEY_USAGE_CERT);
+       if(rc)
+         goto leave;
+      }
+
+    /* Are we a designated revoker for this key? */
+
+    if(!pk->revkey && pk->numrevkeys)
+      BUG();
+
+    for(i=0;i<pk->numrevkeys;i++)
+      {
+       SK_LIST list;
+
+        free_public_key (pk2);
+        pk2 = NULL;
+
+       if(sk_list)
+         {
+           for(list=sk_list;list;list=list->next)
+             {
+               byte fpr[MAX_FINGERPRINT_LEN];
+               size_t fprlen;
+
+               fingerprint_from_pk (list->pk, fpr, &fprlen);
+
+               /* Don't get involved with keys that don't have 160
+                  bit fingerprints */
+               if(fprlen!=20)
+                 continue;
+
+               if(memcmp(fpr,pk->revkey[i].fpr,20)==0)
+                 break;
+             }
+
+           if (list)
+             pk2 = copy_public_key (NULL, list->pk);
+           else
+             continue;
+         }
+       else
+         {
+           pk2 = xmalloc_clear (sizeof *pk2);
+           rc = get_pubkey_byfprint (pk2,
+                                      pk->revkey[i].fpr, MAX_FINGERPRINT_LEN);
+         }
+
+       /* We have the revocation key.  */
+       if(!rc)
+         {
+           PKT_signature *revkey = NULL;
+
+           any = 1;
+
+            print_pubkey_info (NULL, pk);
+           tty_printf ("\n");
+
+           tty_printf (_("To be revoked by:\n"));
+            print_seckey_info (pk2);
+
+           if(pk->revkey[i].class&0x40)
+             tty_printf(_("(This is a sensitive revocation key)\n"));
+           tty_printf("\n");
+
+           if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
+         _("Create a designated revocation certificate for this key? (y/N) ")))
+             continue;
+
+           /* get the reason for the revocation (this is always v4) */
+           reason = ask_revocation_reason( 1, 0, 1 );
+           if( !reason )
+             continue;
+
+           rc = -1;/*FIXME: check_secret_key (pk2, 0 );*/
+           if (rc)
+             continue;
+
+           if( !opt.armor )
+             tty_printf(_("ASCII armored output forced.\n"));
+
+           if( (rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, &out )) )
+             goto leave;
+
+           afx->what = 1;
+           afx->hdrlines = "Comment: A designated revocation certificate"
+             " should follow\n";
+           push_armor_filter (afx, out);
+
+           /* create it */
+           rc = make_keysig_packet( &sig, pk, NULL, NULL, pk2, 0x20, 0,
+                                    0, 0, 0,
+                                    revocation_reason_build_cb, reason,
+                                     NULL);
+           if( rc ) {
+             log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
+             goto leave;
+           }
+
+           /* Spit out a minimal pk as well, since otherwise there is
+               no way to know which key to attach this revocation to.
+               Also include the direct key signature that contains
+               this revocation key.  We're allowed to include
+               sensitive revocation keys along with a revocation, as
+               this may be the only time the recipient has seen it.
+               Note that this means that if we have multiple different
+               sensitive revocation keys in a given direct key
+               signature, we're going to include them all here.  This
+               is annoying, but the good outweighs the bad, since
+               without including this a sensitive revoker can't really
+               do their job.  People should not include multiple
+               sensitive revocation keys in one signature: 2440 says
+               "Note that it may be appropriate to isolate this
+               subpacket within a separate signature so that it is not
+               combined with other subpackets that need to be
+               exported." -dms */
+
+           while(!revkey)
+             {
+               KBNODE signode;
+
+               signode=find_next_kbnode(node,PKT_SIGNATURE);
+               if(!signode)
+                 break;
+
+               node=signode;
+
+               if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
+                  keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
+                  IS_KEY_SIG(signode->pkt->pkt.signature))
+                 {
+                   int j;
+
+                   for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
+                     {
+                       if(pk->revkey[i].class==
+                          signode->pkt->pkt.signature->revkey[j]->class &&
+                          pk->revkey[i].algid==
+                          signode->pkt->pkt.signature->revkey[j]->algid &&
+                          memcmp(pk->revkey[i].fpr,
+                                 signode->pkt->pkt.signature->revkey[j]->fpr,
+                                 MAX_FINGERPRINT_LEN)==0)
+                         {
+                           revkey=signode->pkt->pkt.signature;
+                           break;
+                         }
+                     }
+                 }
+             }
+
+           if(!revkey)
+             BUG();
+
+           rc=export_minimal_pk(out,keyblock,sig,revkey);
+           if(rc)
+             goto leave;
+
+           /* and issue a usage notice */
+           tty_printf(_("Revocation certificate created.\n"));
+           break;
+         }
+      }
 
+    if(!any)
+      log_error(_("no revocation keys found for \"%s\"\n"),uname);
 
   leave:
-    if( pk )
-       free_public_key( pk );
+    free_public_key (pk);
+    free_public_key (pk2);
     if( sig )
        free_seckey_enc( sig );
-    release_kbnode( keyblock );
+
+    release_sk_list(sk_list);
+
     if( rc )
        iobuf_cancel(out);
     else
        iobuf_close(out);
+    release_revocation_reason_info( reason );
+    release_armor_context (afx);
     return rc;
 }
 
-#if 0 /* The code is not complete but anyway, now we use */
-      /* the edit menu to revoke signature */
-/****************
- * Return true if there is already a revocation signature for KEYID
- * in KEYBLOCK at point node.
- */
-static int
-already_revoked( const KBNODE keyblock, const KBNODE node, u32 *keyid )                                                         ) {
-{
-    const KBNODE n = find_prev_kbnode( keyblock, node, PKT_USER_ID );
-
-    for( ; n; n = n->next ) {
-       PKT_signature *sig;
-       if( n->pkt->pkttype == PKT_SIGNATURE
-           && (sig = node->pkt->pkt.signature)->sig_class == 0x30
-           && sig->keyid[0] == keyid[0]
-           && sig->keyid[1] == keyid[1] )
-           return 1;
-       }
-       else if( n->pkt->pkttype == PKT_USER_ID
-           break;
-       else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
-           break;
-    }
-    return 0;
-}
 
 /****************
- * Ask whether the signature should be revoked.  If the user commits this,
- * flag bit 0 is set.
+ * Generate a revocation certificate for UNAME
  */
-static void
-ask_revoke_sig( KBNODE keyblock, KBNODE node, PKT_signature *sig )                                                         ) {
+int
+gen_revoke (const char *uname)
 {
-    KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID );
-
-    if( !unode ) {
-       log_error("Oops: no user ID for signature\n");
-       return;
+  int rc = 0;
+  armor_filter_context_t *afx;
+  PACKET pkt;
+  PKT_public_key *psk;
+  PKT_signature *sig = NULL;
+  u32 keyid[2];
+  iobuf_t out = NULL;
+  kbnode_t keyblock = NULL;
+  kbnode_t node;
+  KEYDB_HANDLE kdbhd;
+  struct revocation_reason_info *reason = NULL;
+  KEYDB_SEARCH_DESC desc;
+
+  if( opt.batch )
+    {
+      log_error(_("can't do this in batch mode\n"));
+      return G10ERR_GENERAL;
     }
 
-    tty_printf(_("user ID: \""));
-    tty_print_utf8_string( unode->pkt->pkt.user_id->name,
-                          unode->pkt->pkt.user_id->len, 0 );
-    tty_printf(_("\"\nsigned with your key %08lX at %s\n"),
-               sig->keyid[1], datestr_from_sig(sig) );
+  afx = new_armor_context ();
+  init_packet( &pkt );
 
-    if( cpr_get_answer_is_yes("ask_revoke_sig.one",
-        _("Create a revocation certificate for this signature? (y/N)")) ) {
-       node->flag |= 1;
+  /* Search the userid; we don't want the whole getkey stuff here.  */
+  kdbhd = keydb_new ();
+  rc = classify_user_id (uname, &desc, 1);
+  if (!rc)
+    rc = keydb_search (kdbhd, &desc, 1, NULL);
+  if (rc)
+    {
+      log_error (_("secret key \"%s\" not found: %s\n"),
+                 uname, g10_errstr (rc));
+      goto leave;
     }
-}
-
-/****************
- * Generate a signature revocation certificate for UNAME
- */
-int
-gen_sig_revoke( const char *uname )
-{
-    int rc = 0;
-    armor_filter_context_t afx;
-    compress_filter_context_t zfx;
-    PACKET pkt;
-    IOBUF out = NULL;
-    KBNODE keyblock = NULL;
-    KBNODE node;
-    KBPOS kbpos;
-    int uidchg;
 
-    if( opt.batch ) {
-       log_error(_("sorry, can't do this in batch mode\n"));
-       return G10ERR_GENERAL;
+  rc = keydb_get_keyblock (kdbhd, &keyblock );
+  if (rc)
+    {
+      log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+      goto leave;
     }
 
+  /* Get the keyid from the keyblock.  */
+  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
+  if (!node)
+    BUG ();
 
-    memset( &afx, 0, sizeof afx);
-    memset( &zfx, 0, sizeof zfx);
-    init_packet( &pkt );
+  psk = node->pkt->pkt.public_key;
+  rc = agent_probe_secret_key (NULL, psk);
+  if (rc)
+    {
+      log_error (_("secret key \"%s\" not found: %s\n"),
+                 uname, gpg_strerror (rc));
+      goto leave;
+    }
 
+  keyid_from_pk (psk, keyid );
+  print_seckey_info (psk);
 
-    /* get the keyblock */
-    rc = find_keyblock_byname( &kbpos, uname );
-    if( rc ) {
-       log_error(_("public key for user `%s' not found\n"), uname );
-       goto leave;
+  tty_printf("\n");
+  if (!cpr_get_answer_is_yes ("gen_revoke.okay",
+                _("Create a revocation certificate for this key? (y/N) ")))
+    {
+      rc = 0;
+      goto leave;
     }
 
-    /* read the keyblock */
-    rc = read_keyblock( &kbpos, &keyblock );
-    if( rc ) {
-       log_error(_("error reading the certificate: %s\n"), g10_errstr(rc) );
-       goto leave;
+  if (psk->version >= 4 || opt.force_v4_certs)
+    {
+      /* Get the reason for the revocation.  */
+      reason = ask_revocation_reason (1, 0, 1);
+      if (!reason)
+        {
+          /* user decided to cancel */
+          rc = 0;
+          goto leave;
+        }
     }
 
-    /* get the keyid from the keyblock */
-    node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
-    if( !node ) {
-       log_error(_("Oops; public key lost!\n"));
-       rc = G10ERR_GENERAL;
-       goto leave;
-    }
+  if (!opt.armor)
+    tty_printf (_("ASCII armored output forced.\n"));
 
-    if( (rc = open_outfile( NULL, 0, &out )) )
-       goto leave;
+  if ((rc = open_outfile (GNUPG_INVALID_FD, NULL, 0, &out )))
+    goto leave;
 
-    if( opt.armor ) {
-       afx.what = 1;
-       iobuf_push_filter( out, armor_filter, &afx );
-    }
+  afx->what = 1;
+  afx->hdrlines = "Comment: A revocation certificate should follow\n";
+  push_armor_filter (afx, out);
 
-    /* Now walk over all signatures which we did with one of
-     * our secret keys.  Hmmm: Should we check for duplicate signatures */
-    clear_kbnode_flags( flags );
-    for( node = keyblock; node; node = node->next ) {
-       PKT_signature *sig;
-       if( node->pkt->pkttype == PKT_SIGNATURE
-           && ((sig = node->pkt->pkt.signature)->sig_class&~3) == 0x10
-           && seckey_available( sig->keyid )
-           && !already_revoked( keyblock, node, sig->keyid ) ) {                                                            ) {
-           ask_revoke_sig( keyblock, node, sig )
-       }
-       else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
-           break;
+  /* create it */
+  rc = make_keysig_packet (&sig, psk, NULL, NULL, psk, 0x20, 0,
+                           opt.force_v4_certs?4:0, 0, 0,
+                           revocation_reason_build_cb, reason, NULL);
+  if (rc)
+    {
+      log_error (_("make_keysig_packet failed: %s\n"), g10_errstr (rc));
+      goto leave;
     }
 
-
-    for( node = keyblock; node; node = node->next ) { {
-       if( (node->flag & 1) )
-           break;
+  if (PGP2 || PGP6 || PGP7 || PGP8)
+    {
+      /* Use a minimal pk for PGPx mode, since PGP can't import bare
+         revocation certificates. */
+      rc = export_minimal_pk (out, keyblock, sig, NULL);
+      if(rc)
+        goto leave;
     }
-    if( !node ) {
-       log_info(_("nothing to revoke\n"));
-       iobuf_cancel(out);
-       out = NULL;
-       goto leave;
+  else
+    {
+      init_packet( &pkt );
+      pkt.pkttype = PKT_SIGNATURE;
+      pkt.pkt.signature = sig;
+
+      rc = build_packet (out, &pkt);
+      if (rc)
+        {
+          log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+          goto leave;
+        }
     }
 
-    init_packet( &pkt );
-    pkt.pkttype = PKT_PUBLIC_KEY;
-    pkt.pkt.public_key = keyblock->pkt->pkt.public_key;
-    rc = build_packet( out, &pkt );
-    if( rc ) {
-       log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
-       goto leave;
-    }
-    uidchg = 1;
-    for( node = keyblock; node; node = node->next ) {
-       if( node->pkt->pkttype == PKT_USER_ID )
-           uidchg = 1;
-       if( !(node->flag & 1) )
-           continue;
-
-       if( uidchg ) {
-           /* create a user ID packet */
-           .......
-           uidchg = 0;
-       }
+  /* and issue a usage notice */
+  tty_printf (_(
+"Revocation certificate created.\n\n"
+"Please move it to a medium which you can hide away; if Mallory gets\n"
+"access to this certificate he can use it to make your key unusable.\n"
+"It is smart to print this certificate and store it away, just in case\n"
+"your media become unreadable.  But have some caution:  The print system of\n"
+"your machine might store the data and make it available to others!\n"));
+
+ leave:
+  if (sig)
+    free_seckey_enc (sig);
+  release_kbnode (keyblock);
+  keydb_release (kdbhd);
+  if (rc)
+    iobuf_cancel(out);
+  else
+    iobuf_close(out);
+  release_revocation_reason_info( reason );
+  release_armor_context (afx);
+  return rc;
+}
+
+
 
-       /* create it */
-       rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x30, 0, NULL, NULL);
-       if( rc ) {
-           log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
-           goto leave;
+struct revocation_reason_info *
+ask_revocation_reason( int key_rev, int cert_rev, int hint )
+{
+    int code=-1;
+    char *description = NULL;
+    struct revocation_reason_info *reason;
+    const char *text_0 = _("No reason specified");
+    const char *text_1 = _("Key has been compromised");
+    const char *text_2 = _("Key is superseded");
+    const char *text_3 = _("Key is no longer used");
+    const char *text_4 = _("User ID is no longer valid");
+    const char *code_text = NULL;
+
+    do {
+        code=-1;
+       xfree(description);
+       description = NULL;
+
+       tty_printf(_("Please select the reason for the revocation:\n"));
+       tty_printf(    "  0 = %s\n", text_0 );
+       if( key_rev )
+           tty_printf("  1 = %s\n", text_1 );
+       if( key_rev )
+           tty_printf("  2 = %s\n", text_2 );
+       if( key_rev )
+           tty_printf("  3 = %s\n", text_3 );
+       if( cert_rev )
+           tty_printf("  4 = %s\n", text_4 );
+       tty_printf(    "  Q = %s\n", _("Cancel") );
+       if( hint )
+           tty_printf(_("(Probably you want to select %d here)\n"), hint );
+
+       while(code==-1) {
+           int n;
+           char *answer = cpr_get("ask_revocation_reason.code",
+                                               _("Your decision? "));
+           trim_spaces( answer );
+           cpr_kill_prompt();
+           if( *answer == 'q' || *answer == 'Q')
+             return NULL; /* cancel */
+           if( hint && !*answer )
+               n = hint;
+           else if(!digitp( answer ) )
+               n = -1;
+           else
+               n = atoi(answer);
+           xfree(answer);
+           if( n == 0 ) {
+               code = 0x00; /* no particular reason */
+               code_text = text_0;
+           }
+           else if( key_rev && n == 1 ) {
+               code = 0x02; /* key has been compromised */
+               code_text = text_1;
+           }
+           else if( key_rev && n == 2 ) {
+               code = 0x01; /* key is superseded */
+               code_text = text_2;
+           }
+           else if( key_rev && n == 3 ) {
+               code = 0x03; /* key is no longer used */
+               code_text = text_3;
+           }
+           else if( cert_rev && n == 4 ) {
+               code = 0x20; /* uid is no longer valid */
+               code_text = text_4;
+           }
+           else
+               tty_printf(_("Invalid selection.\n"));
        }
-       init_packet( &pkt );
-       pkt.pkttype = PKT_SIGNATURE;
-       pkt.pkt.signature = sig;
-
-       rc = build_packet( out, &pkt );
-       if( rc ) {
-           log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
-           goto leave;
+
+       tty_printf(_("Enter an optional description; "
+                    "end it with an empty line:\n") );
+       for(;;) {
+           char *answer = cpr_get("ask_revocation_reason.text", "> " );
+           trim_trailing_ws( answer, strlen(answer) );
+           cpr_kill_prompt();
+           if( !*answer ) {
+               xfree(answer);
+               break;
+           }
+
+           {
+               char *p = make_printable_string( answer, strlen(answer), 0 );
+               xfree(answer);
+               answer = p;
+           }
+
+           if( !description )
+               description = xstrdup(answer);
+           else {
+               char *p = xmalloc( strlen(description) + strlen(answer) + 2 );
+               strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
+               xfree(description);
+               description = p;
+           }
+           xfree(answer);
        }
-    }
 
-  leave:
-    release_kbnode( keyblock );
-    if( !out )
-       ;
-    else if( rc )
-       iobuf_cancel(out);
-    else
-       iobuf_close(out);
-    return rc;
+       tty_printf(_("Reason for revocation: %s\n"), code_text );
+       if( !description )
+           tty_printf(_("(No description given)\n") );
+       else
+           tty_printf("%s\n", description );
+
+    } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
+                                           _("Is this okay? (y/N) "))  );
+
+    reason = xmalloc( sizeof *reason );
+    reason->code = code;
+    reason->desc = description;
+    return reason;
 }
-#endif /* unused code */
 
+void
+release_revocation_reason_info( struct revocation_reason_info *reason )
+{
+    if( reason ) {
+       xfree( reason->desc );
+       xfree( reason );
+    }
+}