d7c32169a116f62b45a241eee6e74a65350716b1
[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);
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);
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);
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_ERROR:
655       opd->only_newsig_seen = 0;
656       /* The error status is informational, so we don't return an
657          error code if we are not ready to process this status. */
658       return sig ? parse_error (sig, args) : 0;
659
660     case GPGME_STATUS_EOF:
661       if (sig && !opd->did_prepare_new_sig)
662         calc_sig_summary (sig);
663       if (opd->only_newsig_seen && sig)
664         {
665           gpgme_signature_t sig2;
666           /* The last signature has no valid information - remove it
667              from the list. */
668           assert (!sig->next);
669           if (sig == opd->result.signatures)
670             opd->result.signatures = NULL;
671           else
672             {
673               for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
674                 if (sig2->next == sig)
675                   {
676                     sig2->next = NULL;
677                     break;
678                   }
679             }
680           /* Note that there is no need to release the members of SIG
681              because we won't be here if they have been set. */
682           free (sig);
683           opd->current_sig = NULL;
684         }
685       opd->only_newsig_seen = 0;
686       break;
687
688     case GPGME_STATUS_PLAINTEXT:
689       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
690       if (err)
691         return err;
692
693     default:
694       break;
695     }
696   return 0;
697 }
698
699
700 static gpgme_error_t
701 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
702 {
703   gpgme_error_t err;
704
705   err = _gpgme_progress_status_handler (priv, code, args);
706   if (!err)
707     err = _gpgme_verify_status_handler (priv, code, args);
708   return err;
709 }
710
711
712 gpgme_error_t
713 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
714 {  
715   void *hook;
716   op_data_t opd;
717
718   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
719                                 sizeof (*opd), release_op_data);
720 }
721
722
723 static gpgme_error_t
724 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
725               gpgme_data_t signed_text, gpgme_data_t plaintext)
726 {
727   gpgme_error_t err;
728
729   err = _gpgme_op_reset (ctx, synchronous);
730   if (err)
731     return err;
732
733   err = _gpgme_op_verify_init_result (ctx);
734   if (err)
735     return err;
736
737   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
738
739   if (!sig)
740     return gpg_error (GPG_ERR_NO_DATA);
741   if (!signed_text && !plaintext)
742     return gpg_error (GPG_ERR_INV_VALUE);
743
744   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
745 }
746
747
748 /* Decrypt ciphertext CIPHER and make a signature verification within
749    CTX and store the resulting plaintext in PLAIN.  */
750 gpgme_error_t
751 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
752                        gpgme_data_t signed_text, gpgme_data_t plaintext)
753 {
754   return verify_start (ctx, 0, sig, signed_text, plaintext);
755 }
756
757
758 /* Decrypt ciphertext CIPHER and make a signature verification within
759    CTX and store the resulting plaintext in PLAIN.  */
760 gpgme_error_t
761 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
762                  gpgme_data_t plaintext)
763 {
764   gpgme_error_t err;
765
766   err = verify_start (ctx, 1, sig, signed_text, plaintext);
767   if (!err)
768     err = _gpgme_wait_one (ctx);
769   return err;
770 }
771
772 \f
773 /* Compatibility interfaces.  */
774
775 /* Get the key used to create signature IDX in CTX and return it in
776    R_KEY.  */
777 gpgme_error_t
778 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
779 {
780   gpgme_verify_result_t result;
781   gpgme_signature_t sig;
782
783   result = gpgme_op_verify_result (ctx);
784   sig = result->signatures;
785
786   while (sig && idx)
787     {
788       sig = sig->next;
789       idx--;
790     }
791   if (!sig || idx)
792     return gpg_error (GPG_ERR_EOF);
793
794   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
795 }
796
797
798 /* Retrieve the signature status of signature IDX in CTX after a
799    successful verify operation in R_STAT (if non-null).  The creation
800    time stamp of the signature is returned in R_CREATED (if non-null).
801    The function returns a string containing the fingerprint.  */
802 const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
803                                   _gpgme_sig_stat_t *r_stat, time_t *r_created)
804 {
805   gpgme_verify_result_t result;
806   gpgme_signature_t sig;
807
808   result = gpgme_op_verify_result (ctx);
809   sig = result->signatures;
810
811   while (sig && idx)
812     {
813       sig = sig->next;
814       idx--;
815     }
816   if (!sig || idx)
817     return NULL;
818
819   if (r_stat)
820     {
821       switch (gpg_err_code (sig->status))
822         {
823         case GPG_ERR_NO_ERROR:
824           *r_stat = GPGME_SIG_STAT_GOOD;
825           break;
826           
827         case GPG_ERR_BAD_SIGNATURE:
828           *r_stat = GPGME_SIG_STAT_BAD;
829           break;
830           
831         case GPG_ERR_NO_PUBKEY:
832           *r_stat = GPGME_SIG_STAT_NOKEY;
833           break;
834           
835         case GPG_ERR_NO_DATA:
836           *r_stat = GPGME_SIG_STAT_NOSIG;
837           break;
838           
839         case GPG_ERR_SIG_EXPIRED:
840           *r_stat = GPGME_SIG_STAT_GOOD_EXP;
841           break;
842           
843         case GPG_ERR_KEY_EXPIRED:
844           *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
845           break;
846           
847         default:
848           *r_stat = GPGME_SIG_STAT_ERROR;
849           break;
850         }
851     }
852   if (r_created)
853     *r_created = sig->timestamp;
854   return sig->fpr;
855 }
856
857
858 /* Retrieve certain attributes of a signature.  IDX is the index
859    number of the signature after a successful verify operation.  WHAT
860    is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
861    one.  WHATIDX is to be passed as 0 for most attributes . */
862 unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
863                                         _gpgme_attr_t what, int whatidx)
864 {
865   gpgme_verify_result_t result;
866   gpgme_signature_t sig;
867
868   result = gpgme_op_verify_result (ctx);
869   sig = result->signatures;
870
871   while (sig && idx)
872     {
873       sig = sig->next;
874       idx--;
875     }
876   if (!sig || idx)
877     return 0;
878
879   switch (what)
880     {
881     case GPGME_ATTR_CREATED:
882       return sig->timestamp;
883
884     case GPGME_ATTR_EXPIRE:
885       return sig->exp_timestamp;
886
887     case GPGME_ATTR_VALIDITY:
888       return (unsigned long) sig->validity;
889
890     case GPGME_ATTR_SIG_STATUS:
891       switch (gpg_err_code (sig->status))
892         {
893         case GPG_ERR_NO_ERROR:
894           return GPGME_SIG_STAT_GOOD;
895           
896         case GPG_ERR_BAD_SIGNATURE:
897           return GPGME_SIG_STAT_BAD;
898           
899         case GPG_ERR_NO_PUBKEY:
900           return GPGME_SIG_STAT_NOKEY;
901           
902         case GPG_ERR_NO_DATA:
903           return GPGME_SIG_STAT_NOSIG;
904           
905         case GPG_ERR_SIG_EXPIRED:
906           return GPGME_SIG_STAT_GOOD_EXP;
907           
908         case GPG_ERR_KEY_EXPIRED:
909           return GPGME_SIG_STAT_GOOD_EXPKEY;
910           
911         default:
912           return GPGME_SIG_STAT_ERROR;
913         }
914
915     case GPGME_ATTR_SIG_SUMMARY:
916       return sig->summary;
917
918     default:
919       break;
920     }
921   return 0;
922 }
923
924
925 const char *gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
926                                       _gpgme_attr_t what, int whatidx)
927 {
928   gpgme_verify_result_t result;
929   gpgme_signature_t sig;
930
931   result = gpgme_op_verify_result (ctx);
932   sig = result->signatures;
933
934   while (sig && idx)
935     {
936       sig = sig->next;
937       idx--;
938     }
939   if (!sig || idx)
940     return NULL;
941
942   switch (what)
943     {
944     case GPGME_ATTR_FPR:
945       return sig->fpr;
946
947     case GPGME_ATTR_ERRTOK:
948       if (whatidx == 1)
949         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
950       else
951         return "";
952     default:
953       break;
954     }
955
956   return NULL;
957 }