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