doc/
[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 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           if (notation->name)
62             free (notation->name);
63           if (notation->value)
64             free (notation->value);
65           notation = next_nota;
66         }
67
68       if (sig->fpr)
69         free (sig->fpr);
70       free (sig);
71       sig = next;
72     }
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       notation = malloc (sizeof (*sig));
432       if (!notation)
433         return gpg_error_from_errno (errno);
434       notation->next = NULL;
435
436       if (code == GPGME_STATUS_NOTATION_NAME)
437         {
438           int len = strlen (args) + 1;
439
440           notation->name = malloc (len);
441           if (!notation->name)
442             {
443               int saved_errno = errno;
444               free (notation);
445               return gpg_error_from_errno (saved_errno);
446             }
447           err = _gpgme_decode_percent_string (args, &notation->name, len);
448           if (err)
449             {
450               free (notation->name);
451               free (notation);
452               return err;
453             }
454
455           notation->value = NULL;
456         }
457       else
458         {
459           int len = strlen (args) + 1;
460
461           notation->name = NULL;
462           notation->value = malloc (len);
463           if (!notation->value)
464             {
465               int saved_errno = errno;
466               free (notation);
467               return gpg_error_from_errno (saved_errno);
468             }
469           err = _gpgme_decode_percent_string (args, &notation->value, len);
470           if (err)
471             {
472               free (notation->value);
473               free (notation);
474               return err;
475             }
476         }
477       *lastp = notation;
478     }
479   else if (code == GPGME_STATUS_NOTATION_DATA)
480     {
481       int len = strlen (args) + 1;
482       char *dest;
483
484       /* FIXME: We could keep a pointer to the last notation in the list.  */
485       while (notation && notation->next)
486         {
487           lastp = &notation->next;
488           notation = notation->next;
489         }
490
491       if (!notation || !notation->name)
492         /* There is notation data without a previous notation
493            name.  The crypto backend misbehaves.  */
494         return gpg_error (GPG_ERR_INV_ENGINE);
495       
496       if (!notation->value)
497         {
498           dest = notation->value = malloc (len);
499           if (!dest)
500             return gpg_error_from_errno (errno);
501         }
502       else
503         {
504           int cur_len = strlen (notation->value);
505           dest = realloc (notation->value, len + strlen (notation->value));
506           if (!dest)
507             return gpg_error_from_errno (errno);
508           notation->value = dest;
509           dest += cur_len;
510         }
511       
512       err = _gpgme_decode_percent_string (args, &dest, len);
513       if (err)
514         return err;
515     }
516   else
517     return gpg_error (GPG_ERR_INV_ENGINE);
518   return 0;
519 }
520
521
522 static gpgme_error_t
523 parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
524 {
525   char *end = strchr (args, ' ');
526
527   if (end)
528     *end = '\0';
529
530   switch (code)
531     {
532     case GPGME_STATUS_TRUST_UNDEFINED:
533     default:
534       sig->validity = GPGME_VALIDITY_UNKNOWN;
535       break;
536
537     case GPGME_STATUS_TRUST_NEVER:
538       sig->validity = GPGME_VALIDITY_NEVER;
539       break;
540
541     case GPGME_STATUS_TRUST_MARGINAL:
542       sig->validity = GPGME_VALIDITY_MARGINAL;
543       break;
544
545     case GPGME_STATUS_TRUST_FULLY:
546     case GPGME_STATUS_TRUST_ULTIMATE:
547       sig->validity = GPGME_VALIDITY_FULL;
548       break;
549     }
550
551   if (*args)
552     sig->validity_reason = _gpgme_map_gnupg_error (args);
553   else
554     sig->validity_reason = 0;
555
556   return 0;
557 }
558
559
560 static gpgme_error_t
561 parse_error (gpgme_signature_t sig, char *args)
562 {
563   gpgme_error_t err;
564   char *where = strchr (args, ' ');
565   char *which;
566
567   if (where)
568     {
569       *where = '\0';
570       which = where + 1;
571
572       where = strchr (which, ' ');
573       if (where)
574         *where = '\0';
575
576       where = args;      
577     }
578   else
579     return gpg_error (GPG_ERR_INV_ENGINE);
580
581   err = _gpgme_map_gnupg_error (which);
582
583   if (!strcmp (where, "verify.findkey"))
584     sig->status = err;
585   else if (!strcmp (where, "verify.keyusage")
586            && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
587     sig->wrong_key_usage = 1;
588
589   return 0;
590 }
591
592
593 gpgme_error_t
594 _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
595 {
596   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
597   gpgme_error_t err;
598   void *hook;
599   op_data_t opd;
600   gpgme_signature_t sig;
601
602   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
603   opd = hook;
604   if (err)
605     return err;
606
607   sig = opd->current_sig;
608
609   switch (code)
610     {
611     case GPGME_STATUS_NEWSIG:
612       if (sig)
613         calc_sig_summary (sig);
614       err = prepare_new_sig (opd);
615       opd->only_newsig_seen = 1;
616       return err;
617
618     case GPGME_STATUS_GOODSIG:
619     case GPGME_STATUS_EXPSIG:
620     case GPGME_STATUS_EXPKEYSIG:
621     case GPGME_STATUS_BADSIG:
622     case GPGME_STATUS_ERRSIG:
623     case GPGME_STATUS_REVKEYSIG:
624       if (sig && !opd->did_prepare_new_sig)
625         calc_sig_summary (sig);
626       opd->only_newsig_seen = 0;
627       return parse_new_sig (opd, code, args);
628
629     case GPGME_STATUS_VALIDSIG:
630       opd->only_newsig_seen = 0;
631       return sig ? parse_valid_sig (sig, args)
632         : gpg_error (GPG_ERR_INV_ENGINE);
633
634     case GPGME_STATUS_NODATA:
635       opd->only_newsig_seen = 0;
636       if (!sig)
637         return gpg_error (GPG_ERR_NO_DATA);
638       sig->status = gpg_error (GPG_ERR_NO_DATA);
639       break;
640
641     case GPGME_STATUS_UNEXPECTED:
642       opd->only_newsig_seen = 0;
643       if (!sig)
644         return gpg_error (GPG_ERR_GENERAL);
645       sig->status = gpg_error (GPG_ERR_NO_DATA);
646       break;
647
648     case GPGME_STATUS_NOTATION_NAME:
649     case GPGME_STATUS_NOTATION_DATA:
650     case GPGME_STATUS_POLICY_URL:
651       opd->only_newsig_seen = 0;
652       return sig ? parse_notation (sig, code, args)
653         : gpg_error (GPG_ERR_INV_ENGINE);
654
655     case GPGME_STATUS_TRUST_UNDEFINED:
656     case GPGME_STATUS_TRUST_NEVER:
657     case GPGME_STATUS_TRUST_MARGINAL:
658     case GPGME_STATUS_TRUST_FULLY:
659     case GPGME_STATUS_TRUST_ULTIMATE:
660       opd->only_newsig_seen = 0;
661       return sig ? parse_trust (sig, code, args)
662         : gpg_error (GPG_ERR_INV_ENGINE);
663
664     case GPGME_STATUS_ERROR:
665       opd->only_newsig_seen = 0;
666       /* The error status is informational, so we don't return an
667          error code if we are not ready to process this status. */
668       return sig ? parse_error (sig, args) : 0;
669
670     case GPGME_STATUS_EOF:
671       if (sig && !opd->did_prepare_new_sig)
672         calc_sig_summary (sig);
673       if (opd->only_newsig_seen && sig)
674         {
675           gpgme_signature_t sig2;
676           /* The last signature has no valid information - remove it
677              from the list. */
678           assert (!sig->next);
679           if (sig == opd->result.signatures)
680             opd->result.signatures = NULL;
681           else
682             {
683               for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
684                 if (sig2->next == sig)
685                   {
686                     sig2->next = NULL;
687                     break;
688                   }
689             }
690           /* Note that there is no need to release the members of SIG
691              because we won't be here if they have been set. */
692           free (sig);
693           opd->current_sig = NULL;
694         }
695       opd->only_newsig_seen = 0;
696       break;
697
698     default:
699       break;
700     }
701   return 0;
702 }
703
704
705 static gpgme_error_t
706 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
707 {
708   gpgme_error_t err;
709
710   err = _gpgme_progress_status_handler (priv, code, args);
711   if (!err)
712     err = _gpgme_verify_status_handler (priv, code, args);
713   return err;
714 }
715
716
717 gpgme_error_t
718 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
719 {  
720   void *hook;
721   op_data_t opd;
722
723   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
724                                 sizeof (*opd), release_op_data);
725 }
726
727
728 static gpgme_error_t
729 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
730               gpgme_data_t signed_text, gpgme_data_t plaintext)
731 {
732   gpgme_error_t err;
733
734   err = _gpgme_op_reset (ctx, synchronous);
735   if (err)
736     return err;
737
738   err = _gpgme_op_verify_init_result (ctx);
739   if (err)
740     return err;
741
742   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
743
744   if (!sig)
745     return gpg_error (GPG_ERR_NO_DATA);
746   if (!signed_text && !plaintext)
747     return gpg_error (GPG_ERR_INV_VALUE);
748
749   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
750 }
751
752
753 /* Decrypt ciphertext CIPHER and make a signature verification within
754    CTX and store the resulting plaintext in PLAIN.  */
755 gpgme_error_t
756 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
757                        gpgme_data_t signed_text, gpgme_data_t plaintext)
758 {
759   return verify_start (ctx, 0, sig, signed_text, plaintext);
760 }
761
762
763 /* Decrypt ciphertext CIPHER and make a signature verification within
764    CTX and store the resulting plaintext in PLAIN.  */
765 gpgme_error_t
766 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
767                  gpgme_data_t plaintext)
768 {
769   gpgme_error_t err;
770
771   err = verify_start (ctx, 1, sig, signed_text, plaintext);
772   if (!err)
773     err = _gpgme_wait_one (ctx);
774   return err;
775 }
776
777 \f
778 /* Compatibility interfaces.  */
779
780 /* Get the key used to create signature IDX in CTX and return it in
781    R_KEY.  */
782 gpgme_error_t
783 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
784 {
785   gpgme_verify_result_t result;
786   gpgme_signature_t sig;
787
788   result = gpgme_op_verify_result (ctx);
789   sig = result->signatures;
790
791   while (sig && idx)
792     {
793       sig = sig->next;
794       idx--;
795     }
796   if (!sig || idx)
797     return gpg_error (GPG_ERR_EOF);
798
799   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
800 }
801
802
803 /* Retrieve the signature status of signature IDX in CTX after a
804    successful verify operation in R_STAT (if non-null).  The creation
805    time stamp of the signature is returned in R_CREATED (if non-null).
806    The function returns a string containing the fingerprint.  */
807 const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
808                                   _gpgme_sig_stat_t *r_stat, time_t *r_created)
809 {
810   gpgme_verify_result_t result;
811   gpgme_signature_t sig;
812
813   result = gpgme_op_verify_result (ctx);
814   sig = result->signatures;
815
816   while (sig && idx)
817     {
818       sig = sig->next;
819       idx--;
820     }
821   if (!sig || idx)
822     return NULL;
823
824   if (r_stat)
825     {
826       switch (gpg_err_code (sig->status))
827         {
828         case GPG_ERR_NO_ERROR:
829           *r_stat = GPGME_SIG_STAT_GOOD;
830           break;
831           
832         case GPG_ERR_BAD_SIGNATURE:
833           *r_stat = GPGME_SIG_STAT_BAD;
834           break;
835           
836         case GPG_ERR_NO_PUBKEY:
837           *r_stat = GPGME_SIG_STAT_NOKEY;
838           break;
839           
840         case GPG_ERR_NO_DATA:
841           *r_stat = GPGME_SIG_STAT_NOSIG;
842           break;
843           
844         case GPG_ERR_SIG_EXPIRED:
845           *r_stat = GPGME_SIG_STAT_GOOD_EXP;
846           break;
847           
848         case GPG_ERR_KEY_EXPIRED:
849           *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
850           break;
851           
852         default:
853           *r_stat = GPGME_SIG_STAT_ERROR;
854           break;
855         }
856     }
857   if (r_created)
858     *r_created = sig->timestamp;
859   return sig->fpr;
860 }
861
862
863 /* Retrieve certain attributes of a signature.  IDX is the index
864    number of the signature after a successful verify operation.  WHAT
865    is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
866    one.  WHATIDX is to be passed as 0 for most attributes . */
867 unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
868                                         _gpgme_attr_t what, int whatidx)
869 {
870   gpgme_verify_result_t result;
871   gpgme_signature_t sig;
872
873   result = gpgme_op_verify_result (ctx);
874   sig = result->signatures;
875
876   while (sig && idx)
877     {
878       sig = sig->next;
879       idx--;
880     }
881   if (!sig || idx)
882     return 0;
883
884   switch (what)
885     {
886     case GPGME_ATTR_CREATED:
887       return sig->timestamp;
888
889     case GPGME_ATTR_EXPIRE:
890       return sig->exp_timestamp;
891
892     case GPGME_ATTR_VALIDITY:
893       return (unsigned long) sig->validity;
894
895     case GPGME_ATTR_SIG_STATUS:
896       switch (gpg_err_code (sig->status))
897         {
898         case GPG_ERR_NO_ERROR:
899           return GPGME_SIG_STAT_GOOD;
900           
901         case GPG_ERR_BAD_SIGNATURE:
902           return GPGME_SIG_STAT_BAD;
903           
904         case GPG_ERR_NO_PUBKEY:
905           return GPGME_SIG_STAT_NOKEY;
906           
907         case GPG_ERR_NO_DATA:
908           return GPGME_SIG_STAT_NOSIG;
909           
910         case GPG_ERR_SIG_EXPIRED:
911           return GPGME_SIG_STAT_GOOD_EXP;
912           
913         case GPG_ERR_KEY_EXPIRED:
914           return GPGME_SIG_STAT_GOOD_EXPKEY;
915           
916         default:
917           return GPGME_SIG_STAT_ERROR;
918         }
919
920     case GPGME_ATTR_SIG_SUMMARY:
921       return sig->summary;
922
923     default:
924       break;
925     }
926   return 0;
927 }
928
929
930 const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
931                                       _gpgme_attr_t what, int whatidx)
932 {
933   gpgme_verify_result_t result;
934   gpgme_signature_t sig;
935
936   result = gpgme_op_verify_result (ctx);
937   sig = result->signatures;
938
939   while (sig && idx)
940     {
941       sig = sig->next;
942       idx--;
943     }
944   if (!sig || idx)
945     return NULL;
946
947   switch (what)
948     {
949     case GPGME_ATTR_FPR:
950       return sig->fpr;
951
952     case GPGME_ATTR_ERRTOK:
953       if (whatidx == 1)
954         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
955       else
956         return "";
957     default:
958       break;
959     }
960
961   return NULL;
962 }