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