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