* verify.c (parse_error): Compare only the last part of the where
[gpgme.git] / gpgme / verify.c
1 /* verify.c - Signature verification.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11  
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "gpgme.h"
30 #include "util.h"
31 #include "context.h"
32 #include "ops.h"
33
34 \f
35 typedef struct
36 {
37   struct _gpgme_op_verify_result result;
38
39   gpgme_signature_t current_sig;
40   int did_prepare_new_sig;
41 } *op_data_t;
42
43
44 static void
45 release_op_data (void *hook)
46 {
47   op_data_t opd = (op_data_t) hook;
48   gpgme_signature_t sig = opd->result.signatures;
49
50   while (sig)
51     {
52       gpgme_signature_t next = sig->next;
53       gpgme_sig_notation_t notation = sig->notations;
54
55       while (notation)
56         {
57           gpgme_sig_notation_t next_nota = notation->next;
58
59           if (notation->name)
60             free (notation->name);
61           if (notation->value)
62             free (notation->value);
63           notation = next_nota;
64         }
65
66       if (sig->fpr)
67         free (sig->fpr);
68       free (sig);
69       sig = next;
70     }
71 }
72
73
74 gpgme_verify_result_t
75 gpgme_op_verify_result (gpgme_ctx_t ctx)
76 {
77   void *hook;
78   op_data_t opd;
79   gpgme_error_t err;
80
81   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
82   opd = hook;
83   if (err || !opd)
84     return NULL;
85
86   return &opd->result;
87 }
88
89 \f
90 /* Build a summary vector from RESULT. */
91 static void
92 calc_sig_summary (gpgme_signature_t sig)
93 {
94   unsigned long sum = 0;
95
96   if (sig->validity == GPGME_VALIDITY_FULL
97       || sig->validity == GPGME_VALIDITY_ULTIMATE)
98     {
99       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
100           || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
101           || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
102         sum |= GPGME_SIGSUM_GREEN;
103     }
104   else if (sig->validity == GPGME_VALIDITY_NEVER)
105     {
106       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
107           || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
108           || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
109         sum |= GPGME_SIGSUM_RED;
110     }
111   else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
112     sum |= GPGME_SIGSUM_RED;
113   
114   /* FIXME: handle the case when key and message are expired. */
115   switch (gpg_err_code (sig->status))
116     {
117     case GPG_ERR_SIG_EXPIRED:
118       sum |= GPGME_SIGSUM_SIG_EXPIRED;
119       break;
120
121     case GPG_ERR_KEY_EXPIRED:
122       sum |= GPGME_SIGSUM_KEY_EXPIRED;
123       break;
124
125     case GPG_ERR_NO_PUBKEY:
126       sum |= GPGME_SIGSUM_KEY_MISSING;
127       break;
128
129     case GPG_ERR_BAD_SIGNATURE:
130     case GPG_ERR_NO_ERROR:
131       break;
132
133     default:
134       sum |= GPGME_SIGSUM_SYS_ERROR;
135       break;
136     }
137   
138   if (sig->wrong_key_usage)
139     sum |= GPGME_SIGSUM_BAD_POLICY;
140   
141   /* Set the valid flag when the signature is unquestionable
142      valid. */
143   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
144     sum |= GPGME_SIGSUM_VALID;
145   
146   sig->summary = sum;
147 }
148   
149
150 static gpgme_error_t
151 prepare_new_sig (op_data_t opd)
152 {
153   gpgme_signature_t sig;
154   
155   sig = calloc (1, sizeof (*sig));
156   if (!sig)
157     return gpg_error_from_errno (errno);
158   if (!opd->result.signatures)
159     opd->result.signatures = sig;
160   if (opd->current_sig)
161     opd->current_sig->next = sig;
162   opd->current_sig = sig;
163   opd->did_prepare_new_sig = 1;
164   return 0;
165 }
166
167 static gpgme_error_t
168 parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
169 {
170   gpgme_signature_t sig;
171   char *end = strchr (args, ' ');
172
173   if (end)
174     {
175       *end = '\0';
176       end++;
177     }
178
179   if (!opd->did_prepare_new_sig)
180     {
181       gpg_error_t err;
182
183       err = prepare_new_sig (opd);
184       if (err)
185         return err;
186     }
187   assert (opd->did_prepare_new_sig);
188   opd->did_prepare_new_sig = 0;
189
190   assert (opd->current_sig);
191   sig = opd->current_sig;
192
193   /* FIXME: We should set the source of the state.  */
194   switch (code)
195     {
196     case GPGME_STATUS_GOODSIG:
197       sig->status = gpg_error (GPG_ERR_NO_ERROR);
198       break;
199
200     case GPGME_STATUS_EXPSIG:
201       sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
202       break;
203
204     case GPGME_STATUS_EXPKEYSIG:
205       sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
206       break;
207
208     case GPGME_STATUS_BADSIG:
209       sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
210       break;
211
212     case GPGME_STATUS_ERRSIG:
213       if (end)
214         {
215           int i = 0;
216           /* The return code is the 6th argument, if it is 9, the
217              problem is a missing key.  */
218           while (end && i < 4)
219             {
220               end = strchr (end, ' ');
221               if (end)
222                 end++;
223               i++;
224             }
225           if (end && end[0] && (!end[1] || end[1] == ' '))
226             {
227               switch (end[0])
228                 {
229                 case '4':
230                   sig->status = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
231                   break;
232                   
233                 case '9':
234                   sig->status = gpg_error (GPG_ERR_NO_PUBKEY);
235                   break;
236                   
237                 default:
238                   sig->status = gpg_error (GPG_ERR_GENERAL);
239                 }
240             }
241         }
242       else
243         sig->status = gpg_error (GPG_ERR_GENERAL);
244       break;
245
246     default:
247       return gpg_error (GPG_ERR_GENERAL);
248     }
249
250   if (*args)
251     {
252       sig->fpr = strdup (args);
253       if (!sig->fpr)
254         return gpg_error_from_errno (errno);
255     }
256   return 0;
257 }
258
259
260 static gpgme_error_t
261 parse_valid_sig (gpgme_signature_t sig, char *args)
262 {
263   char *end = strchr (args, ' ');
264
265   if (end)
266     {
267       *end = '\0';
268       end++;
269     }
270
271   if (!*args)
272     /* We require at least the fingerprint.  */
273     return gpg_error (GPG_ERR_GENERAL);
274
275   if (sig->fpr)
276     free (sig->fpr);
277   sig->fpr = strdup (args);
278   if (!sig->fpr)
279     return gpg_error_from_errno (errno);
280
281   end = strchr (end, ' ');
282   if (end)
283     {
284       char *tail;
285
286       sig->timestamp = _gpgme_parse_timestamp (end, &tail);
287       if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
288         return gpg_error (GPG_ERR_INV_ENGINE);
289       end = tail;
290      
291       sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
292       if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
293         return gpg_error (GPG_ERR_INV_ENGINE);
294     }
295   return 0;
296 }
297
298
299 static gpgme_error_t
300 parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
301 {
302   gpgme_error_t err;
303   gpgme_sig_notation_t *lastp = &sig->notations;
304   gpgme_sig_notation_t notation = sig->notations;
305   char *end = strchr (args, ' ');
306
307   if (end)
308     *end = '\0';
309
310   if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
311     {
312       /* FIXME: We could keep a pointer to the last notation in the list.  */
313       while (notation && notation->value)
314         {
315           lastp = &notation->next;
316           notation = notation->next;
317         }
318
319       if (notation)
320         /* There is another notation name without data for the
321            previous one.  The crypto backend misbehaves.  */
322         return gpg_error (GPG_ERR_INV_ENGINE);
323
324       notation = malloc (sizeof (*sig));
325       if (!notation)
326         return gpg_error_from_errno (errno);
327       notation->next = NULL;
328
329       if (code == GPGME_STATUS_NOTATION_NAME)
330         {
331           int len = strlen (args) + 1;
332
333           notation->name = malloc (len);
334           if (!notation->name)
335             {
336               int saved_errno = errno;
337               free (notation);
338               return gpg_error_from_errno (saved_errno);
339             }
340           err = _gpgme_decode_percent_string (args, &notation->name, len);
341           if (err)
342             {
343               free (notation->name);
344               free (notation);
345               return err;
346             }
347
348           notation->value = NULL;
349         }
350       else
351         {
352           int len = strlen (args) + 1;
353
354           notation->name = NULL;
355           notation->value = malloc (len);
356           if (!notation->value)
357             {
358               int saved_errno = errno;
359               free (notation);
360               return gpg_error_from_errno (saved_errno);
361             }
362           err = _gpgme_decode_percent_string (args, &notation->value, len);
363           if (err)
364             {
365               free (notation->value);
366               free (notation);
367               return err;
368             }
369         }
370       *lastp = notation;
371     }
372   else if (code == GPGME_STATUS_NOTATION_DATA)
373     {
374       int len = strlen (args) + 1;
375       char *dest;
376
377       /* FIXME: We could keep a pointer to the last notation in the list.  */
378       while (notation && notation->next)
379         {
380           lastp = &notation->next;
381           notation = notation->next;
382         }
383
384       if (!notation || !notation->name)
385         /* There is notation data without a previous notation
386            name.  The crypto backend misbehaves.  */
387         return gpg_error (GPG_ERR_INV_ENGINE);
388       
389       if (!notation->value)
390         {
391           dest = notation->value = malloc (len);
392           if (!dest)
393             return gpg_error_from_errno (errno);
394         }
395       else
396         {
397           int cur_len = strlen (notation->value);
398           dest = realloc (notation->value, len + strlen (notation->value));
399           if (!dest)
400             return gpg_error_from_errno (errno);
401           notation->value = dest;
402           dest += cur_len;
403         }
404       
405       err = _gpgme_decode_percent_string (args, &dest, len);
406       if (err)
407         return err;
408     }
409   else
410     return gpg_error (GPG_ERR_INV_ENGINE);
411   return 0;
412 }
413
414
415 static gpgme_error_t
416 parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
417 {
418   char *end = strchr (args, ' ');
419
420   if (end)
421     *end = '\0';
422
423   switch (code)
424     {
425     case GPGME_STATUS_TRUST_UNDEFINED:
426     default:
427       sig->validity = GPGME_VALIDITY_UNKNOWN;
428       break;
429
430     case GPGME_STATUS_TRUST_NEVER:
431       sig->validity = GPGME_VALIDITY_NEVER;
432       break;
433
434     case GPGME_STATUS_TRUST_MARGINAL:
435       sig->validity = GPGME_VALIDITY_MARGINAL;
436       break;
437
438     case GPGME_STATUS_TRUST_FULLY:
439     case GPGME_STATUS_TRUST_ULTIMATE:
440       sig->validity = GPGME_VALIDITY_FULL;
441       break;
442     }
443
444   if (*args)
445     sig->validity_reason = _gpgme_map_gnupg_error (args);
446
447   return 0;
448 }
449
450
451 static gpgme_error_t
452 parse_error (gpgme_signature_t sig, char *args)
453 {
454   gpgme_error_t err;
455   char *where = strchr (args, ' ');
456   char *which;
457   char *where_last;
458
459   if (where)
460     {
461       *where = '\0';
462       which = where + 1;
463
464       where = strchr (which, ' ');
465       if (where)
466         *where = '\0';
467
468       where = args;      
469     }
470   else
471     return gpg_error (GPG_ERR_INV_ENGINE);
472
473   /* It is often useful to compare only the last part of the where token. */
474   where_last = strrchr (where, '.');
475   if (!where_last)
476     where_last = where;
477
478   err = _gpgme_map_gnupg_error (which);
479
480   if (!strcmp (where_last, ".findkey"))
481     sig->status = err;
482   else if (!strcmp (where_last, ".keyusage")
483            && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
484     sig->wrong_key_usage = 1;
485
486   return 0;
487 }
488
489
490 gpgme_error_t
491 _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
492 {
493   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
494   gpgme_error_t err;
495   void *hook;
496   op_data_t opd;
497   gpgme_signature_t sig;
498
499   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
500   opd = hook;
501   if (err)
502     return err;
503
504   sig = opd->current_sig;
505
506   switch (code)
507     {
508     case GPGME_STATUS_NEWSIG:
509       if (sig)
510         calc_sig_summary (sig);
511       break;
512
513     case GPGME_STATUS_GOODSIG:
514     case GPGME_STATUS_EXPSIG:
515     case GPGME_STATUS_EXPKEYSIG:
516     case GPGME_STATUS_BADSIG:
517     case GPGME_STATUS_ERRSIG:
518       if (sig && !opd->did_prepare_new_sig)
519         calc_sig_summary (sig);
520       return parse_new_sig (opd, code, args);
521
522     case GPGME_STATUS_VALIDSIG:
523       return sig ? parse_valid_sig (sig, args)
524         : gpg_error (GPG_ERR_INV_ENGINE);
525
526     case GPGME_STATUS_NODATA:
527       if (!sig)
528         return gpg_error (GPG_ERR_NO_DATA);
529       sig->status = gpg_error (GPG_ERR_NO_DATA);
530       break;
531
532     case GPGME_STATUS_UNEXPECTED:
533       if (!sig)
534         return gpg_error (GPG_ERR_GENERAL);
535       sig->status = gpg_error (GPG_ERR_NO_DATA);
536       break;
537
538     case GPGME_STATUS_NOTATION_NAME:
539     case GPGME_STATUS_NOTATION_DATA:
540     case GPGME_STATUS_POLICY_URL:
541       return sig ? parse_notation (sig, code, args)
542         : gpg_error (GPG_ERR_INV_ENGINE);
543
544     case GPGME_STATUS_TRUST_UNDEFINED:
545     case GPGME_STATUS_TRUST_NEVER:
546     case GPGME_STATUS_TRUST_MARGINAL:
547     case GPGME_STATUS_TRUST_FULLY:
548     case GPGME_STATUS_TRUST_ULTIMATE:
549       return sig ? parse_trust (sig, code, args)
550         : gpg_error (GPG_ERR_INV_ENGINE);
551
552     case GPGME_STATUS_ERROR:
553       return sig ? parse_error (sig, args) : gpg_error (GPG_ERR_INV_ENGINE);
554
555     case GPGME_STATUS_EOF:
556       if (sig && !opd->did_prepare_new_sig)
557         calc_sig_summary (sig);
558       break;
559
560     default:
561       break;
562     }
563   return 0;
564 }
565
566
567 static gpgme_error_t
568 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
569 {
570   gpgme_error_t err;
571
572   err = _gpgme_progress_status_handler (priv, code, args);
573   if (!err)
574     err = _gpgme_verify_status_handler (priv, code, args);
575   return err;
576 }
577
578
579 gpgme_error_t
580 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
581 {  
582   void *hook;
583   op_data_t opd;
584
585   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
586                                 sizeof (*opd), release_op_data);
587 }
588
589
590 static gpgme_error_t
591 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
592               gpgme_data_t signed_text, gpgme_data_t plaintext)
593 {
594   gpgme_error_t err;
595
596   err = _gpgme_op_reset (ctx, synchronous);
597   if (err)
598     return err;
599
600   err = _gpgme_op_verify_init_result (ctx);
601   if (err)
602     return err;
603
604   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
605
606   if (!sig)
607     return gpg_error (GPG_ERR_NO_DATA);
608   if (!signed_text && !plaintext)
609     return gpg_error (GPG_ERR_INV_VALUE);
610
611   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
612 }
613
614
615 /* Decrypt ciphertext CIPHER and make a signature verification within
616    CTX and store the resulting plaintext in PLAIN.  */
617 gpgme_error_t
618 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
619                        gpgme_data_t signed_text, gpgme_data_t plaintext)
620 {
621   return verify_start (ctx, 0, sig, signed_text, plaintext);
622 }
623
624
625 /* Decrypt ciphertext CIPHER and make a signature verification within
626    CTX and store the resulting plaintext in PLAIN.  */
627 gpgme_error_t
628 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
629                  gpgme_data_t plaintext)
630 {
631   gpgme_error_t err;
632
633   err = verify_start (ctx, 1, sig, signed_text, plaintext);
634   if (!err)
635     err = _gpgme_wait_one (ctx);
636   return err;
637 }
638
639 \f
640 /* Compatibility interfaces.  */
641
642 /* Get the key used to create signature IDX in CTX and return it in
643    R_KEY.  */
644 gpgme_error_t
645 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
646 {
647   gpgme_verify_result_t result;
648   gpgme_signature_t sig;
649
650   result = gpgme_op_verify_result (ctx);
651   sig = result->signatures;
652
653   while (sig && idx)
654     {
655       sig = sig->next;
656       idx--;
657     }
658   if (!sig || idx)
659     return gpg_error (GPG_ERR_EOF);
660
661   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
662 }
663
664
665 /* Retrieve the signature status of signature IDX in CTX after a
666    successful verify operation in R_STAT (if non-null).  The creation
667    time stamp of the signature is returned in R_CREATED (if non-null).
668    The function returns a string containing the fingerprint.  */
669 const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
670                                   _gpgme_sig_stat_t *r_stat, time_t *r_created)
671 {
672   gpgme_verify_result_t result;
673   gpgme_signature_t sig;
674
675   result = gpgme_op_verify_result (ctx);
676   sig = result->signatures;
677
678   while (sig && idx)
679     {
680       sig = sig->next;
681       idx--;
682     }
683   if (!sig || idx)
684     return NULL;
685
686   if (r_stat)
687     {
688       switch (gpg_err_code (sig->status))
689         {
690         case GPG_ERR_NO_ERROR:
691           *r_stat = GPGME_SIG_STAT_GOOD;
692           break;
693           
694         case GPG_ERR_BAD_SIGNATURE:
695           *r_stat = GPGME_SIG_STAT_BAD;
696           break;
697           
698         case GPG_ERR_NO_PUBKEY:
699           *r_stat = GPGME_SIG_STAT_NOKEY;
700           break;
701           
702         case GPG_ERR_NO_DATA:
703           *r_stat = GPGME_SIG_STAT_NOSIG;
704           break;
705           
706         case GPG_ERR_SIG_EXPIRED:
707           *r_stat = GPGME_SIG_STAT_GOOD_EXP;
708           break;
709           
710         case GPG_ERR_KEY_EXPIRED:
711           *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
712           break;
713           
714         default:
715           *r_stat = GPGME_SIG_STAT_ERROR;
716           break;
717         }
718     }
719   if (r_created)
720     *r_created = sig->timestamp;
721   return sig->fpr;
722 }
723
724
725 /* Retrieve certain attributes of a signature.  IDX is the index
726    number of the signature after a successful verify operation.  WHAT
727    is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
728    one.  WHATIDX is to be passed as 0 for most attributes . */
729 unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
730                                         _gpgme_attr_t what, int whatidx)
731 {
732   gpgme_verify_result_t result;
733   gpgme_signature_t sig;
734
735   result = gpgme_op_verify_result (ctx);
736   sig = result->signatures;
737
738   while (sig && idx)
739     {
740       sig = sig->next;
741       idx--;
742     }
743   if (!sig || idx)
744     return 0;
745
746   switch (what)
747     {
748     case GPGME_ATTR_CREATED:
749       return sig->timestamp;
750
751     case GPGME_ATTR_EXPIRE:
752       return sig->exp_timestamp;
753
754     case GPGME_ATTR_VALIDITY:
755       return (unsigned long) sig->validity;
756
757     case GPGME_ATTR_SIG_STATUS:
758       switch (gpg_err_code (sig->status))
759         {
760         case GPG_ERR_NO_ERROR:
761           return GPGME_SIG_STAT_GOOD;
762           
763         case GPG_ERR_BAD_SIGNATURE:
764           return GPGME_SIG_STAT_BAD;
765           
766         case GPG_ERR_NO_PUBKEY:
767           return GPGME_SIG_STAT_NOKEY;
768           
769         case GPG_ERR_NO_DATA:
770           return GPGME_SIG_STAT_NOSIG;
771           
772         case GPG_ERR_SIG_EXPIRED:
773           return GPGME_SIG_STAT_GOOD_EXP;
774           
775         case GPG_ERR_KEY_EXPIRED:
776           return GPGME_SIG_STAT_GOOD_EXPKEY;
777           
778         default:
779           return GPGME_SIG_STAT_ERROR;
780         }
781
782     case GPGME_ATTR_SIG_SUMMARY:
783       return sig->summary;
784
785     default:
786       break;
787     }
788   return 0;
789 }
790
791
792 const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
793                                       _gpgme_attr_t what, int whatidx)
794 {
795   gpgme_verify_result_t result;
796   gpgme_signature_t sig;
797
798   result = gpgme_op_verify_result (ctx);
799   sig = result->signatures;
800
801   while (sig && idx)
802     {
803       sig = sig->next;
804       idx--;
805     }
806   if (!sig || idx)
807     return NULL;
808
809   switch (what)
810     {
811     case GPGME_ATTR_FPR:
812       return sig->fpr;
813
814     case GPGME_ATTR_ERRTOK:
815       if (whatidx == 1)
816         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
817       else
818         return "";
819     default:
820       break;
821     }
822
823   return NULL;
824 }