gpg: In search-keys return "Not found" instead of "No Data".
[gnupg.git] / g10 / revoke.c
1 /* revoke.c - Create recovation certificates.
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
3  *               2004 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <ctype.h>
27
28 #include "gpg.h"
29 #include "options.h"
30 #include "packet.h"
31 #include "../common/status.h"
32 #include "keydb.h"
33 #include "../common/util.h"
34 #include "main.h"
35 #include "../common/ttyio.h"
36 #include "../common/i18n.h"
37 #include "call-agent.h"
38
39 struct revocation_reason_info {
40     int code;
41     char *desc;
42 };
43
44
45 int
46 revocation_reason_build_cb( PKT_signature *sig, void *opaque )
47 {
48     struct revocation_reason_info *reason = opaque;
49     char *ud = NULL;
50     byte *buffer;
51     size_t buflen = 1;
52
53     if(!reason)
54       return 0;
55
56     if( reason->desc ) {
57         ud = native_to_utf8( reason->desc );
58         buflen += strlen(ud);
59     }
60     buffer = xmalloc( buflen );
61     *buffer = reason->code;
62     if( ud ) {
63         memcpy(buffer+1, ud, strlen(ud) );
64         xfree( ud );
65     }
66
67     build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
68     xfree( buffer );
69     return 0;
70 }
71
72 /* Outputs a minimal pk (as defined by 2440) from a keyblock.  A
73    minimal pk consists of the public key packet and a user ID.  We try
74    and pick a user ID that has a uid signature, and include it if
75    possible. */
76 static int
77 export_minimal_pk(IOBUF out,KBNODE keyblock,
78                   PKT_signature *revsig,PKT_signature *revkey)
79 {
80   KBNODE node;
81   PACKET pkt;
82   PKT_user_id *uid=NULL;
83   PKT_signature *selfsig=NULL;
84   u32 keyid[2];
85   int rc;
86
87   node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
88   if(!node)
89     {
90       log_error("key incomplete\n");
91       return GPG_ERR_GENERAL;
92     }
93
94   keyid_from_pk(node->pkt->pkt.public_key,keyid);
95
96   pkt=*node->pkt;
97   rc=build_packet(out,&pkt);
98   if(rc)
99     {
100       log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
101       return rc;
102     }
103
104   init_packet(&pkt);
105   pkt.pkttype=PKT_SIGNATURE;
106
107   /* the revocation itself, if any.  2440 likes this to come first. */
108   if(revsig)
109     {
110       pkt.pkt.signature=revsig;
111       rc=build_packet(out,&pkt);
112       if(rc)
113         {
114           log_error("build_packet failed: %s\n", gpg_strerror (rc) );
115           return rc;
116         }
117     }
118
119   /* If a revkey in a 1F sig is present, include it too */
120   if(revkey)
121     {
122       pkt.pkt.signature=revkey;
123       rc=build_packet(out,&pkt);
124       if(rc)
125         {
126           log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
127           return rc;
128         }
129     }
130
131   while(!selfsig)
132     {
133       KBNODE signode;
134
135       node=find_next_kbnode(node,PKT_USER_ID);
136       if(!node)
137         {
138           /* We're out of user IDs - none were self-signed. */
139           if(uid)
140             break;
141           else
142             {
143               log_error(_("key %s has no user IDs\n"),keystr(keyid));
144               return GPG_ERR_GENERAL;
145             }
146         }
147
148       if(node->pkt->pkt.user_id->attrib_data)
149         continue;
150
151       uid=node->pkt->pkt.user_id;
152       signode=node;
153
154       while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
155         {
156           if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
157              keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
158              IS_UID_SIG(signode->pkt->pkt.signature))
159             {
160               selfsig=signode->pkt->pkt.signature;
161               break;
162             }
163         }
164     }
165
166   pkt.pkttype=PKT_USER_ID;
167   pkt.pkt.user_id=uid;
168
169   rc=build_packet(out,&pkt);
170   if(rc)
171     {
172       log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
173       return rc;
174     }
175
176   if(selfsig)
177     {
178       pkt.pkttype=PKT_SIGNATURE;
179       pkt.pkt.signature=selfsig;
180
181       rc=build_packet(out,&pkt);
182       if(rc)
183         {
184           log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) );
185           return rc;
186         }
187     }
188
189   return 0;
190 }
191
192 /****************
193  * Generate a revocation certificate for UNAME via a designated revoker
194  */
195 int
196 gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr)
197 {
198     int rc = 0;
199     armor_filter_context_t *afx;
200     PKT_public_key *pk = NULL;
201     PKT_public_key *pk2 = NULL;
202     PKT_signature *sig = NULL;
203     IOBUF out = NULL;
204     struct revocation_reason_info *reason = NULL;
205     KEYDB_HANDLE kdbhd;
206     KEYDB_SEARCH_DESC desc;
207     KBNODE keyblock=NULL,node;
208     u32 keyid[2];
209     int i,any=0;
210     SK_LIST sk_list=NULL;
211
212     if( opt.batch )
213       {
214         log_error(_("can't do this in batch mode\n"));
215         return GPG_ERR_GENERAL;
216       }
217
218     afx = new_armor_context ();
219
220     kdbhd = keydb_new ();
221     if (!kdbhd)
222       {
223         rc = gpg_error_from_syserror ();
224         goto leave;
225       }
226     rc = classify_user_id (uname, &desc, 1);
227     if (!rc)
228       rc = keydb_search (kdbhd, &desc, 1, NULL);
229     if (rc) {
230         log_error (_("key \"%s\" not found: %s\n"),uname, gpg_strerror (rc));
231         goto leave;
232     }
233
234     rc = keydb_get_keyblock (kdbhd, &keyblock );
235     if( rc ) {
236         log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
237         goto leave;
238     }
239
240     /* To parse the revkeys */
241     merge_keys_and_selfsig (ctrl, keyblock);
242
243     /* get the key from the keyblock */
244     node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
245     if( !node )
246       BUG ();
247
248     pk=node->pkt->pkt.public_key;
249
250     keyid_from_pk(pk,keyid);
251
252     if(locusr)
253       {
254         rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT);
255         if(rc)
256           goto leave;
257       }
258
259     /* Are we a designated revoker for this key? */
260
261     if(!pk->revkey && pk->numrevkeys)
262       BUG();
263
264     for(i=0;i<pk->numrevkeys;i++)
265       {
266         SK_LIST list;
267
268         free_public_key (pk2);
269         pk2 = NULL;
270
271         if(sk_list)
272           {
273             for(list=sk_list;list;list=list->next)
274               {
275                 byte fpr[MAX_FINGERPRINT_LEN];
276                 size_t fprlen;
277
278                 fingerprint_from_pk (list->pk, fpr, &fprlen);
279
280                 /* Don't get involved with keys that don't have a v4
281                  * or v5 fingerprint */
282                 if (fprlen != 20 && fprlen != 32)
283                   continue;
284
285                 if (!memcmp(fpr,pk->revkey[i].fpr, fprlen))
286                   break;
287               }
288
289             if (list)
290               pk2 = copy_public_key (NULL, list->pk);
291             else
292               continue;
293           }
294         else
295           {
296             pk2 = xmalloc_clear (sizeof *pk2);
297             rc = get_pubkey_byfprint (ctrl, pk2, NULL,
298                                       pk->revkey[i].fpr, pk->revkey[i].fprlen);
299           }
300
301         /* We have the revocation key.  */
302         if(!rc)
303           {
304             PKT_signature *revkey = NULL;
305
306             any = 1;
307
308             print_pubkey_info (ctrl, NULL, pk);
309             tty_printf ("\n");
310
311             tty_printf (_("To be revoked by:\n"));
312             print_seckey_info (ctrl, pk2);
313
314             if(pk->revkey[i].class&0x40)
315               tty_printf(_("(This is a sensitive revocation key)\n"));
316             tty_printf("\n");
317
318             rc = agent_probe_secret_key (ctrl, pk2);
319             if (rc)
320               {
321                 tty_printf (_("Secret key is not available.\n"));
322                 continue;
323               }
324
325             if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
326          _("Create a designated revocation certificate for this key? (y/N) ")))
327               continue;
328
329             /* get the reason for the revocation (this is always v4) */
330             reason = ask_revocation_reason( 1, 0, 1 );
331             if( !reason )
332               continue;
333
334             if( !opt.armor )
335               tty_printf(_("ASCII armored output forced.\n"));
336
337             if( (rc = open_outfile (-1, NULL, 0, 1, &out )) )
338               goto leave;
339
340             afx->what = 1;
341             afx->hdrlines = "Comment: A designated revocation certificate"
342               " should follow\n";
343             push_armor_filter (afx, out);
344
345             /* create it */
346             rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, 0,
347                                      0, 0,
348                                      revocation_reason_build_cb, reason,
349                                      NULL);
350             if( rc ) {
351               log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc));
352               goto leave;
353             }
354
355             /* Spit out a minimal pk as well, since otherwise there is
356                no way to know which key to attach this revocation to.
357                Also include the direct key signature that contains
358                this revocation key.  We're allowed to include
359                sensitive revocation keys along with a revocation, as
360                this may be the only time the recipient has seen it.
361                Note that this means that if we have multiple different
362                sensitive revocation keys in a given direct key
363                signature, we're going to include them all here.  This
364                is annoying, but the good outweighs the bad, since
365                without including this a sensitive revoker can't really
366                do their job.  People should not include multiple
367                sensitive revocation keys in one signature: 2440 says
368                "Note that it may be appropriate to isolate this
369                subpacket within a separate signature so that it is not
370                combined with other subpackets that need to be
371                exported." -dms */
372
373             while(!revkey)
374               {
375                 KBNODE signode;
376
377                 signode=find_next_kbnode(node,PKT_SIGNATURE);
378                 if(!signode)
379                   break;
380
381                 node=signode;
382
383                 if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
384                    keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
385                    IS_KEY_SIG(signode->pkt->pkt.signature))
386                   {
387                     int j;
388
389                     for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
390                       {
391                         if (pk->revkey[i].class
392                                == signode->pkt->pkt.signature->revkey[j].class
393                             && pk->revkey[i].algid
394                                 == signode->pkt->pkt.signature->revkey[j].algid
395                             && pk->revkey[i].fprlen
396                                == signode->pkt->pkt.signature->revkey[j].fprlen
397                             && !memcmp
398                                  (pk->revkey[i].fpr,
399                                   signode->pkt->pkt.signature->revkey[j].fpr,
400                                   pk->revkey[i].fprlen))
401                           {
402                             revkey = signode->pkt->pkt.signature;
403                             break;
404                           }
405                       }
406                   }
407               }
408
409             if(!revkey)
410               BUG();
411
412             rc=export_minimal_pk(out,keyblock,sig,revkey);
413             if(rc)
414               goto leave;
415
416             /* and issue a usage notice */
417             tty_printf(_("Revocation certificate created.\n"));
418             break;
419           }
420       }
421
422     if(!any)
423       log_error(_("no revocation keys found for \"%s\"\n"),uname);
424
425   leave:
426     free_public_key (pk);
427     free_public_key (pk2);
428     if( sig )
429         free_seckey_enc( sig );
430
431     release_sk_list(sk_list);
432
433     if( rc )
434         iobuf_cancel(out);
435     else
436         iobuf_close(out);
437     release_revocation_reason_info( reason );
438     release_armor_context (afx);
439     return rc;
440 }
441
442
443 /* Common core to create the revocation. FILENAME may be NULL to write
444    to stdout or the filename given by --output.  REASON describes the
445    revocation reason.  PSK is the public primary key - we expect that
446    a corresponding secret key is available.  KEYBLOCK is the entire
447    KEYBLOCK which is used in PGP mode to write a minimal key and not
448    just the naked revocation signature; it may be NULL.  If LEADINTEXT
449    is not NULL, it is written right before the (armored) output.*/
450 static int
451 create_revocation (ctrl_t ctrl,
452                    const char *filename,
453                    struct revocation_reason_info *reason,
454                    PKT_public_key *psk,
455                    kbnode_t keyblock,
456                    const char *leadintext, int suffix,
457                    const char *cache_nonce)
458 {
459   int rc;
460   iobuf_t out = NULL;
461   armor_filter_context_t *afx;
462   PKT_signature *sig = NULL;
463   PACKET pkt;
464
465   afx = new_armor_context ();
466
467   if ((rc = open_outfile (-1, filename, suffix, 1, &out)))
468     goto leave;
469
470   if (leadintext )
471     iobuf_writestr (out, leadintext);
472
473   afx->what = 1;
474   afx->hdrlines = "Comment: This is a revocation certificate\n";
475   push_armor_filter (afx, out);
476
477   rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, 0,
478                            0, 0,
479                            revocation_reason_build_cb, reason, cache_nonce);
480   if (rc)
481     {
482       log_error (_("make_keysig_packet failed: %s\n"), gpg_strerror (rc));
483       goto leave;
484     }
485
486   if (keyblock && (PGP7 || PGP8))
487     {
488       /* Use a minimal pk for PGPx mode, since PGP can't import bare
489          revocation certificates. */
490       rc = export_minimal_pk (out, keyblock, sig, NULL);
491       if (rc)
492         goto leave;
493     }
494   else
495     {
496       init_packet (&pkt);
497       pkt.pkttype = PKT_SIGNATURE;
498       pkt.pkt.signature = sig;
499
500       rc = build_packet (out, &pkt);
501       if (rc)
502         {
503           log_error (_("build_packet failed: %s\n"), gpg_strerror (rc));
504           goto leave;
505         }
506     }
507
508  leave:
509   if (sig)
510     free_seckey_enc (sig);
511   if (rc)
512     iobuf_cancel (out);
513   else
514     iobuf_close (out);
515   release_armor_context (afx);
516   return rc;
517 }
518
519
520 /* This function is used to generate a standard revocation certificate
521    by gpg's interactive key generation function.  The certificate is
522    stored at a dedicated place in a slightly modified form to avoid an
523    accidental import.  PSK is the primary key; a corresponding secret
524    key must be available.  CACHE_NONCE is optional but can be used to
525    help gpg-agent to avoid an extra passphrase prompt. */
526 int
527 gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce)
528 {
529   int rc;
530   estream_t memfp;
531   struct revocation_reason_info reason;
532   char *dir, *tmpstr, *fname;
533   void *leadin;
534   size_t len;
535   u32 keyid[2];
536   int kl;
537   char *orig_codeset;
538   char *old_outfile;
539
540   dir = get_openpgp_revocdir (gnupg_homedir ());
541   tmpstr = hexfingerprint (psk, NULL, 0);
542   if (!tmpstr)
543     {
544       rc = gpg_error_from_syserror ();
545       xfree (dir);
546       return rc;
547     }
548   fname = strconcat (dir, DIRSEP_S, tmpstr, NULL);
549   if (!fname)
550     {
551       rc = gpg_error_from_syserror ();
552       xfree (tmpstr);
553       xfree (dir);
554       return rc;
555     }
556   xfree (tmpstr);
557   xfree (dir);
558
559   keyid_from_pk (psk, keyid);
560
561   memfp = es_fopenmem (0, "r+");
562   if (!memfp)
563     log_fatal ("error creating memory stream\n");
564
565   orig_codeset = i18n_switchto_utf8 ();
566
567   es_fprintf (memfp, "%s\n\n",
568               _("This is a revocation certificate for the OpenPGP key:"));
569
570   print_key_line (ctrl, memfp, psk, 0);
571
572   if (opt.keyid_format != KF_NONE)
573     print_fingerprint (ctrl, memfp, psk, 3);
574
575   kl = opt.keyid_format == KF_NONE? 0 : keystrlen ();
576
577   tmpstr = get_user_id (ctrl, keyid, &len, NULL);
578   es_fprintf (memfp, "uid%*s%.*s\n\n",
579               kl + 10, "",
580               (int)len, tmpstr);
581   xfree (tmpstr);
582
583   es_fprintf (memfp, "%s\n\n%s\n\n%s\n\n:",
584      _("A revocation certificate is a kind of \"kill switch\" to publicly\n"
585        "declare that a key shall not anymore be used.  It is not possible\n"
586        "to retract such a revocation certificate once it has been published."),
587      _("Use it to revoke this key in case of a compromise or loss of\n"
588        "the secret key.  However, if the secret key is still accessible,\n"
589        "it is better to generate a new revocation certificate and give\n"
590        "a reason for the revocation.  For details see the description of\n"
591        "of the gpg command \"--generate-revocation\" in the "
592        "GnuPG manual."),
593      _("To avoid an accidental use of this file, a colon has been inserted\n"
594        "before the 5 dashes below.  Remove this colon with a text editor\n"
595        "before importing and publishing this revocation certificate."));
596
597   es_putc (0, memfp);
598
599   i18n_switchback (orig_codeset);
600
601   if (es_fclose_snatch (memfp, &leadin, NULL))
602     log_fatal ("error snatching memory stream\n");
603
604   reason.code = 0x00; /* No particular reason.  */
605   reason.desc = NULL;
606   old_outfile = opt.outfile;
607   opt.outfile = NULL;
608   rc = create_revocation (ctrl,
609                           fname, &reason, psk, NULL, leadin, 3, cache_nonce);
610   opt.outfile = old_outfile;
611   if (!rc && !opt.quiet)
612     log_info (_("revocation certificate stored as '%s.rev'\n"), fname);
613
614   xfree (leadin);
615   xfree (fname);
616
617   return rc;
618 }
619
620
621
622 /****************
623  * Generate a revocation certificate for UNAME
624  */
625 int
626 gen_revoke (ctrl_t ctrl, const char *uname)
627 {
628   int rc = 0;
629   PKT_public_key *psk;
630   u32 keyid[2];
631   kbnode_t keyblock = NULL;
632   kbnode_t node;
633   KEYDB_HANDLE kdbhd;
634   struct revocation_reason_info *reason = NULL;
635   KEYDB_SEARCH_DESC desc;
636
637   if( opt.batch )
638     {
639       log_error(_("can't do this in batch mode\n"));
640       return GPG_ERR_GENERAL;
641     }
642
643   /* Search the userid; we don't want the whole getkey stuff here.  */
644   kdbhd = keydb_new ();
645   if (!kdbhd)
646     {
647       rc = gpg_error_from_syserror ();
648       goto leave;
649     }
650   rc = classify_user_id (uname, &desc, 1);
651   if (!rc)
652     rc = keydb_search (kdbhd, &desc, 1, NULL);
653   if (rc)
654     {
655       if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
656         log_error (_("secret key \"%s\" not found\n"), uname);
657       else
658         log_error (_("secret key \"%s\" not found: %s\n"),
659                    uname, gpg_strerror (rc));
660       goto leave;
661     }
662
663   rc = keydb_get_keyblock (kdbhd, &keyblock );
664   if (rc)
665     {
666       log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) );
667       goto leave;
668     }
669
670   rc = keydb_search (kdbhd, &desc, 1, NULL);
671   if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
672     /* Not ambiguous.  */
673     {
674     }
675   else if (rc == 0)
676     /* Ambiguous.  */
677     {
678       char *info;
679
680       /* TRANSLATORS: The %s prints a key specification which
681          for example has been given at the command line.  Several lines
682          lines with secret key infos are printed after this message.  */
683       log_error (_("'%s' matches multiple secret keys:\n"), uname);
684
685       info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key);
686       log_error ("  %s\n", info);
687       xfree (info);
688       release_kbnode (keyblock);
689
690       rc = keydb_get_keyblock (kdbhd, &keyblock);
691       while (! rc)
692         {
693           info = format_seckey_info (ctrl, keyblock->pkt->pkt.public_key);
694           log_info ("  %s\n", info);
695           xfree (info);
696           release_kbnode (keyblock);
697           keyblock = NULL;
698
699           rc = keydb_search (kdbhd, &desc, 1, NULL);
700           if (! rc)
701             rc = keydb_get_keyblock (kdbhd, &keyblock);
702         }
703
704       rc = GPG_ERR_AMBIGUOUS_NAME;
705
706       goto leave;
707     }
708   else
709     {
710       log_error (_("error searching the keyring: %s\n"), gpg_strerror (rc));
711       goto leave;
712     }
713
714   /* Get the keyid from the keyblock.  */
715   node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
716   if (!node)
717     BUG ();
718
719   psk = node->pkt->pkt.public_key;
720   rc = agent_probe_secret_key (NULL, psk);
721   if (rc)
722     {
723       log_error (_("secret key \"%s\" not found: %s\n"),
724                  uname, gpg_strerror (rc));
725       goto leave;
726     }
727
728   keyid_from_pk (psk, keyid );
729   print_seckey_info (ctrl, psk);
730
731   tty_printf("\n");
732   if (!cpr_get_answer_is_yes ("gen_revoke.okay",
733                 _("Create a revocation certificate for this key? (y/N) ")))
734     {
735       rc = 0;
736       goto leave;
737     }
738
739   /* Get the reason for the revocation.  */
740   reason = ask_revocation_reason (1, 0, 1);
741   if (!reason)
742     {
743       /* User decided to cancel.  */
744       rc = 0;
745       goto leave;
746     }
747
748   if (!opt.armor)
749     tty_printf (_("ASCII armored output forced.\n"));
750
751   rc = create_revocation (ctrl, NULL, reason, psk, keyblock, NULL, 0, NULL);
752   if (rc)
753     goto leave;
754
755   /* and issue a usage notice */
756   tty_printf (_(
757 "Revocation certificate created.\n\n"
758 "Please move it to a medium which you can hide away; if Mallory gets\n"
759 "access to this certificate he can use it to make your key unusable.\n"
760 "It is smart to print this certificate and store it away, just in case\n"
761 "your media become unreadable.  But have some caution:  The print system of\n"
762 "your machine might store the data and make it available to others!\n"));
763
764  leave:
765   release_kbnode (keyblock);
766   keydb_release (kdbhd);
767   release_revocation_reason_info( reason );
768   return rc;
769 }
770
771
772
773 struct revocation_reason_info *
774 ask_revocation_reason( int key_rev, int cert_rev, int hint )
775 {
776     int code=-1;
777     char *description = NULL;
778     struct revocation_reason_info *reason;
779     const char *text_0 = _("No reason specified");
780     const char *text_1 = _("Key has been compromised");
781     const char *text_2 = _("Key is superseded");
782     const char *text_3 = _("Key is no longer used");
783     const char *text_4 = _("User ID is no longer valid");
784     const char *code_text = NULL;
785
786     do {
787         code=-1;
788         xfree(description);
789         description = NULL;
790
791         tty_printf(_("Please select the reason for the revocation:\n"));
792         tty_printf(    "  0 = %s\n", text_0 );
793         if( key_rev )
794             tty_printf("  1 = %s\n", text_1 );
795         if( key_rev )
796             tty_printf("  2 = %s\n", text_2 );
797         if( key_rev )
798             tty_printf("  3 = %s\n", text_3 );
799         if( cert_rev )
800             tty_printf("  4 = %s\n", text_4 );
801         tty_printf(    "  Q = %s\n", _("Cancel") );
802         if( hint )
803             tty_printf(_("(Probably you want to select %d here)\n"), hint );
804
805         while(code==-1) {
806             int n;
807             char *answer = cpr_get("ask_revocation_reason.code",
808                                                 _("Your decision? "));
809             trim_spaces( answer );
810             cpr_kill_prompt();
811             if( *answer == 'q' || *answer == 'Q')
812               return NULL; /* cancel */
813             if( hint && !*answer )
814                 n = hint;
815             else if(!digitp( answer ) )
816                 n = -1;
817             else
818                 n = atoi(answer);
819             xfree(answer);
820             if( n == 0 ) {
821                 code = 0x00; /* no particular reason */
822                 code_text = text_0;
823             }
824             else if( key_rev && n == 1 ) {
825                 code = 0x02; /* key has been compromised */
826                 code_text = text_1;
827             }
828             else if( key_rev && n == 2 ) {
829                 code = 0x01; /* key is superseded */
830                 code_text = text_2;
831             }
832             else if( key_rev && n == 3 ) {
833                 code = 0x03; /* key is no longer used */
834                 code_text = text_3;
835             }
836             else if( cert_rev && n == 4 ) {
837                 code = 0x20; /* uid is no longer valid */
838                 code_text = text_4;
839             }
840             else
841                 tty_printf(_("Invalid selection.\n"));
842         }
843
844         tty_printf(_("Enter an optional description; "
845                      "end it with an empty line:\n") );
846         for(;;) {
847             char *answer = cpr_get("ask_revocation_reason.text", "> " );
848             trim_trailing_ws( answer, strlen(answer) );
849             cpr_kill_prompt();
850             if( !*answer ) {
851                 xfree(answer);
852                 break;
853             }
854
855             {
856                 char *p = make_printable_string( answer, strlen(answer), 0 );
857                 xfree(answer);
858                 answer = p;
859             }
860
861             if( !description )
862                 description = xstrdup(answer);
863             else {
864                 char *p = xmalloc( strlen(description) + strlen(answer) + 2 );
865                 strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
866                 xfree(description);
867                 description = p;
868             }
869             xfree(answer);
870         }
871
872         tty_printf(_("Reason for revocation: %s\n"), code_text );
873         if( !description )
874             tty_printf(_("(No description given)\n") );
875         else
876             tty_printf("%s\n", description );
877
878     } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
879                                             _("Is this okay? (y/N) "))  );
880
881     reason = xmalloc( sizeof *reason );
882     reason->code = code;
883     reason->desc = description;
884     return reason;
885 }
886
887 struct revocation_reason_info *
888 get_default_uid_revocation_reason(void)
889 {
890   struct revocation_reason_info *reason;
891   reason = xmalloc( sizeof *reason );
892   reason->code = 0x20; /* uid is no longer valid */
893   reason->desc = strdup(""); /* no text */
894   return reason;
895 }
896
897 void
898 release_revocation_reason_info( struct revocation_reason_info *reason )
899 {
900     if( reason ) {
901         xfree( reason->desc );
902         xfree( reason );
903     }
904 }