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