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