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