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