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