a63a564f2eba27a88fdca7d2d10099bd427a3b1c
[gpgme.git] / gpgme / verify.c
1 /* verify.c - Signature verification.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004 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       return prepare_new_sig (opd);
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       /* The error status is informational, so we don't return an
554          error code if we are not ready to process this status. */
555       return sig ? parse_error (sig, args) : 0;
556
557     case GPGME_STATUS_EOF:
558       if (sig && !opd->did_prepare_new_sig)
559         calc_sig_summary (sig);
560       break;
561
562     default:
563       break;
564     }
565   return 0;
566 }
567
568
569 static gpgme_error_t
570 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
571 {
572   gpgme_error_t err;
573
574   err = _gpgme_progress_status_handler (priv, code, args);
575   if (!err)
576     err = _gpgme_verify_status_handler (priv, code, args);
577   return err;
578 }
579
580
581 gpgme_error_t
582 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
583 {  
584   void *hook;
585   op_data_t opd;
586
587   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
588                                 sizeof (*opd), release_op_data);
589 }
590
591
592 static gpgme_error_t
593 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
594               gpgme_data_t signed_text, gpgme_data_t plaintext)
595 {
596   gpgme_error_t err;
597
598   err = _gpgme_op_reset (ctx, synchronous);
599   if (err)
600     return err;
601
602   err = _gpgme_op_verify_init_result (ctx);
603   if (err)
604     return err;
605
606   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
607
608   if (!sig)
609     return gpg_error (GPG_ERR_NO_DATA);
610   if (!signed_text && !plaintext)
611     return gpg_error (GPG_ERR_INV_VALUE);
612
613   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
614 }
615
616
617 /* Decrypt ciphertext CIPHER and make a signature verification within
618    CTX and store the resulting plaintext in PLAIN.  */
619 gpgme_error_t
620 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
621                        gpgme_data_t signed_text, gpgme_data_t plaintext)
622 {
623   return verify_start (ctx, 0, sig, signed_text, plaintext);
624 }
625
626
627 /* Decrypt ciphertext CIPHER and make a signature verification within
628    CTX and store the resulting plaintext in PLAIN.  */
629 gpgme_error_t
630 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
631                  gpgme_data_t plaintext)
632 {
633   gpgme_error_t err;
634
635   err = verify_start (ctx, 1, sig, signed_text, plaintext);
636   if (!err)
637     err = _gpgme_wait_one (ctx);
638   return err;
639 }
640
641 \f
642 /* Compatibility interfaces.  */
643
644 /* Get the key used to create signature IDX in CTX and return it in
645    R_KEY.  */
646 gpgme_error_t
647 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
648 {
649   gpgme_verify_result_t result;
650   gpgme_signature_t sig;
651
652   result = gpgme_op_verify_result (ctx);
653   sig = result->signatures;
654
655   while (sig && idx)
656     {
657       sig = sig->next;
658       idx--;
659     }
660   if (!sig || idx)
661     return gpg_error (GPG_ERR_EOF);
662
663   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
664 }
665
666
667 /* Retrieve the signature status of signature IDX in CTX after a
668    successful verify operation in R_STAT (if non-null).  The creation
669    time stamp of the signature is returned in R_CREATED (if non-null).
670    The function returns a string containing the fingerprint.  */
671 const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
672                                   _gpgme_sig_stat_t *r_stat, time_t *r_created)
673 {
674   gpgme_verify_result_t result;
675   gpgme_signature_t sig;
676
677   result = gpgme_op_verify_result (ctx);
678   sig = result->signatures;
679
680   while (sig && idx)
681     {
682       sig = sig->next;
683       idx--;
684     }
685   if (!sig || idx)
686     return NULL;
687
688   if (r_stat)
689     {
690       switch (gpg_err_code (sig->status))
691         {
692         case GPG_ERR_NO_ERROR:
693           *r_stat = GPGME_SIG_STAT_GOOD;
694           break;
695           
696         case GPG_ERR_BAD_SIGNATURE:
697           *r_stat = GPGME_SIG_STAT_BAD;
698           break;
699           
700         case GPG_ERR_NO_PUBKEY:
701           *r_stat = GPGME_SIG_STAT_NOKEY;
702           break;
703           
704         case GPG_ERR_NO_DATA:
705           *r_stat = GPGME_SIG_STAT_NOSIG;
706           break;
707           
708         case GPG_ERR_SIG_EXPIRED:
709           *r_stat = GPGME_SIG_STAT_GOOD_EXP;
710           break;
711           
712         case GPG_ERR_KEY_EXPIRED:
713           *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
714           break;
715           
716         default:
717           *r_stat = GPGME_SIG_STAT_ERROR;
718           break;
719         }
720     }
721   if (r_created)
722     *r_created = sig->timestamp;
723   return sig->fpr;
724 }
725
726
727 /* Retrieve certain attributes of a signature.  IDX is the index
728    number of the signature after a successful verify operation.  WHAT
729    is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
730    one.  WHATIDX is to be passed as 0 for most attributes . */
731 unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
732                                         _gpgme_attr_t what, int whatidx)
733 {
734   gpgme_verify_result_t result;
735   gpgme_signature_t sig;
736
737   result = gpgme_op_verify_result (ctx);
738   sig = result->signatures;
739
740   while (sig && idx)
741     {
742       sig = sig->next;
743       idx--;
744     }
745   if (!sig || idx)
746     return 0;
747
748   switch (what)
749     {
750     case GPGME_ATTR_CREATED:
751       return sig->timestamp;
752
753     case GPGME_ATTR_EXPIRE:
754       return sig->exp_timestamp;
755
756     case GPGME_ATTR_VALIDITY:
757       return (unsigned long) sig->validity;
758
759     case GPGME_ATTR_SIG_STATUS:
760       switch (gpg_err_code (sig->status))
761         {
762         case GPG_ERR_NO_ERROR:
763           return GPGME_SIG_STAT_GOOD;
764           
765         case GPG_ERR_BAD_SIGNATURE:
766           return GPGME_SIG_STAT_BAD;
767           
768         case GPG_ERR_NO_PUBKEY:
769           return GPGME_SIG_STAT_NOKEY;
770           
771         case GPG_ERR_NO_DATA:
772           return GPGME_SIG_STAT_NOSIG;
773           
774         case GPG_ERR_SIG_EXPIRED:
775           return GPGME_SIG_STAT_GOOD_EXP;
776           
777         case GPG_ERR_KEY_EXPIRED:
778           return GPGME_SIG_STAT_GOOD_EXPKEY;
779           
780         default:
781           return GPGME_SIG_STAT_ERROR;
782         }
783
784     case GPGME_ATTR_SIG_SUMMARY:
785       return sig->summary;
786
787     default:
788       break;
789     }
790   return 0;
791 }
792
793
794 const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
795                                       _gpgme_attr_t what, int whatidx)
796 {
797   gpgme_verify_result_t result;
798   gpgme_signature_t sig;
799
800   result = gpgme_op_verify_result (ctx);
801   sig = result->signatures;
802
803   while (sig && idx)
804     {
805       sig = sig->next;
806       idx--;
807     }
808   if (!sig || idx)
809     return NULL;
810
811   switch (what)
812     {
813     case GPGME_ATTR_FPR:
814       return sig->fpr;
815
816     case GPGME_ATTR_ERRTOK:
817       if (whatidx == 1)
818         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
819       else
820         return "";
821     default:
822       break;
823     }
824
825   return NULL;
826 }