1 /* verify.c - Signature verification.
2 Copyright (C) 2000 Werner Koch (dd9jn)
3 Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
5 This file is part of GPGME.
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.
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.
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
39 struct _gpgme_op_verify_result result;
41 gpgme_signature_t current_sig;
42 int did_prepare_new_sig;
49 release_op_data (void *hook)
51 op_data_t opd = (op_data_t) hook;
52 gpgme_signature_t sig = opd->result.signatures;
56 gpgme_signature_t next = sig->next;
57 gpgme_sig_notation_t notation = sig->notations;
61 gpgme_sig_notation_t next_nota = notation->next;
63 _gpgme_sig_notation_free (notation);
70 free (sig->pka_address);
75 if (opd->result.file_name)
76 free (opd->result.file_name);
81 gpgme_op_verify_result (gpgme_ctx_t ctx)
86 gpgme_signature_t sig;
88 TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
89 err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
93 TRACE_SUC0 ("result=(null)");
97 /* It is possible that we saw a new signature only followed by an
98 ERROR line for that. In particular a missing X.509 key triggers
99 this. In this case it is surprising that the summary field has
100 not been updated. We fix it here by explicitly looking for this
101 case. The real fix would be to have GPGME emit ERRSIG. */
102 for (sig = opd->result.signatures; sig; sig = sig->next)
106 switch (gpg_err_code (sig->status))
108 case GPG_ERR_KEY_EXPIRED:
109 sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
112 case GPG_ERR_NO_PUBKEY:
113 sig->summary |= GPGME_SIGSUM_KEY_MISSING;
122 /* Now for some tracing stuff. */
123 if (_gpgme_debug_trace ())
127 for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
129 TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
130 i, sig->fpr, sig->summary, gpg_strerror (sig->status));
131 TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
132 i, sig->timestamp, sig->exp_timestamp,
133 sig->wrong_key_usage ? "wrong key usage" : "",
134 sig->pka_trust == 1 ? "pka bad"
135 : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
136 sig->chain_model ? "chain model" : "");
137 TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
138 i, sig->validity, gpg_strerror (sig->validity_reason),
139 gpgme_pubkey_algo_name (sig->pubkey_algo),
140 gpgme_hash_algo_name (sig->hash_algo));
141 if (sig->pka_address)
143 TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
147 TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
152 TRACE_SUC1 ("result=%p", &opd->result);
157 /* Build a summary vector from RESULT. */
159 calc_sig_summary (gpgme_signature_t sig)
161 unsigned long sum = 0;
163 /* Calculate the red/green flag. */
164 if (sig->validity == GPGME_VALIDITY_FULL
165 || sig->validity == GPGME_VALIDITY_ULTIMATE)
167 if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
168 || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
169 || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
170 sum |= GPGME_SIGSUM_GREEN;
172 else if (sig->validity == GPGME_VALIDITY_NEVER)
174 if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
175 || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
176 || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
177 sum |= GPGME_SIGSUM_RED;
179 else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
180 sum |= GPGME_SIGSUM_RED;
183 /* FIXME: handle the case when key and message are expired. */
184 switch (gpg_err_code (sig->status))
186 case GPG_ERR_SIG_EXPIRED:
187 sum |= GPGME_SIGSUM_SIG_EXPIRED;
190 case GPG_ERR_KEY_EXPIRED:
191 sum |= GPGME_SIGSUM_KEY_EXPIRED;
194 case GPG_ERR_NO_PUBKEY:
195 sum |= GPGME_SIGSUM_KEY_MISSING;
198 case GPG_ERR_BAD_SIGNATURE:
199 case GPG_ERR_NO_ERROR:
203 sum |= GPGME_SIGSUM_SYS_ERROR;
207 /* Now look at the certain reason codes. */
208 switch (gpg_err_code (sig->validity_reason))
210 case GPG_ERR_CRL_TOO_OLD:
211 if (sig->validity == GPGME_VALIDITY_UNKNOWN)
212 sum |= GPGME_SIGSUM_CRL_TOO_OLD;
215 case GPG_ERR_CERT_REVOKED:
216 sum |= GPGME_SIGSUM_KEY_REVOKED;
223 /* Check other flags. */
224 if (sig->wrong_key_usage)
225 sum |= GPGME_SIGSUM_BAD_POLICY;
227 /* Set the valid flag when the signature is unquestionable
228 valid. (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */
229 if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
230 sum |= GPGME_SIGSUM_VALID;
237 prepare_new_sig (op_data_t opd)
239 gpgme_signature_t sig;
241 if (opd->only_newsig_seen && opd->current_sig)
243 /* We have only seen the NEWSIG status and nothing else - we
244 better skip this signature therefore and reuse it for the
245 next possible signature. */
246 sig = opd->current_sig;
247 memset (sig, 0, sizeof *sig);
248 assert (opd->result.signatures == sig);
252 sig = calloc (1, sizeof (*sig));
254 return gpg_error_from_syserror ();
255 if (!opd->result.signatures)
256 opd->result.signatures = sig;
257 if (opd->current_sig)
258 opd->current_sig->next = sig;
259 opd->current_sig = sig;
261 opd->did_prepare_new_sig = 1;
262 opd->only_newsig_seen = 0;
267 parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
269 gpgme_signature_t sig;
270 char *end = strchr (args, ' ');
279 if (!opd->did_prepare_new_sig)
283 err = prepare_new_sig (opd);
287 assert (opd->did_prepare_new_sig);
288 opd->did_prepare_new_sig = 0;
290 assert (opd->current_sig);
291 sig = opd->current_sig;
293 /* FIXME: We should set the source of the state. */
296 case GPGME_STATUS_GOODSIG:
297 sig->status = gpg_error (GPG_ERR_NO_ERROR);
300 case GPGME_STATUS_EXPSIG:
301 sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
304 case GPGME_STATUS_EXPKEYSIG:
305 sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
308 case GPGME_STATUS_BADSIG:
309 sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
312 case GPGME_STATUS_REVKEYSIG:
313 sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
316 case GPGME_STATUS_ERRSIG:
317 /* Parse the pubkey algo. */
319 goto parse_err_sig_fail;
320 gpg_err_set_errno (0);
321 sig->pubkey_algo = strtol (end, &tail, 0);
322 if (errno || end == tail || *tail != ' ')
323 goto parse_err_sig_fail;
328 /* Parse the hash algo. */
330 goto parse_err_sig_fail;
331 gpg_err_set_errno (0);
332 sig->hash_algo = strtol (end, &tail, 0);
333 if (errno || end == tail || *tail != ' ')
334 goto parse_err_sig_fail;
339 /* Skip the sig class. */
340 end = strchr (end, ' ');
342 goto parse_err_sig_fail;
346 /* Parse the timestamp. */
347 sig->timestamp = _gpgme_parse_timestamp (end, &tail);
348 if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
349 return gpg_error (GPG_ERR_INV_ENGINE);
354 /* Parse the return code. */
355 if (end[0] && (!end[1] || end[1] == ' '))
360 sig->status = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
364 sig->status = gpg_error (GPG_ERR_NO_PUBKEY);
368 sig->status = gpg_error (GPG_ERR_GENERAL);
372 goto parse_err_sig_fail;
374 goto parse_err_sig_ok;
377 sig->status = gpg_error (GPG_ERR_GENERAL);
382 return gpg_error (GPG_ERR_GENERAL);
387 sig->fpr = strdup (args);
389 return gpg_error_from_syserror ();
396 parse_valid_sig (gpgme_signature_t sig, char *args)
398 char *end = strchr (args, ' ');
406 /* We require at least the fingerprint. */
407 return gpg_error (GPG_ERR_GENERAL);
411 sig->fpr = strdup (args);
413 return gpg_error_from_syserror ();
415 /* Skip the creation date. */
416 end = strchr (end, ' ');
421 sig->timestamp = _gpgme_parse_timestamp (end, &tail);
422 if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
423 return gpg_error (GPG_ERR_INV_ENGINE);
426 sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
427 if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
428 return gpg_error (GPG_ERR_INV_ENGINE);
433 /* Skip the signature version. */
434 end = strchr (end, ' ');
440 /* Skip the reserved field. */
441 end = strchr (end, ' ');
444 /* Parse the pubkey algo. */
445 gpg_err_set_errno (0);
446 sig->pubkey_algo = strtol (end, &tail, 0);
447 if (errno || end == tail || *tail != ' ')
448 return gpg_error (GPG_ERR_INV_ENGINE);
456 /* Parse the hash algo. */
458 gpg_err_set_errno (0);
459 sig->hash_algo = strtol (end, &tail, 0);
460 if (errno || end == tail || *tail != ' ')
461 return gpg_error (GPG_ERR_INV_ENGINE);
472 parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
475 gpgme_sig_notation_t *lastp = &sig->notations;
476 gpgme_sig_notation_t notation = sig->notations;
477 char *end = strchr (args, ' ');
482 if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
484 /* FIXME: We could keep a pointer to the last notation in the list. */
485 while (notation && notation->value)
487 lastp = ¬ation->next;
488 notation = notation->next;
492 /* There is another notation name without data for the
493 previous one. The crypto backend misbehaves. */
494 return gpg_error (GPG_ERR_INV_ENGINE);
496 err = _gpgme_sig_notation_create (¬ation, NULL, 0, NULL, 0, 0);
500 if (code == GPGME_STATUS_NOTATION_NAME)
502 err = _gpgme_decode_percent_string (args, ¬ation->name, 0, 0);
505 _gpgme_sig_notation_free (notation);
509 notation->name_len = strlen (notation->name);
511 /* FIXME: For now we fake the human-readable flag. The
512 critical flag can not be reported as it is not
514 notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
515 notation->human_readable = 1;
519 /* This is a policy URL. */
521 err = _gpgme_decode_percent_string (args, ¬ation->value, 0, 0);
524 _gpgme_sig_notation_free (notation);
528 notation->value_len = strlen (notation->value);
532 else if (code == GPGME_STATUS_NOTATION_DATA)
534 int len = strlen (args) + 1;
537 /* FIXME: We could keep a pointer to the last notation in the list. */
538 while (notation && notation->next)
540 lastp = ¬ation->next;
541 notation = notation->next;
544 if (!notation || !notation->name)
545 /* There is notation data without a previous notation
546 name. The crypto backend misbehaves. */
547 return gpg_error (GPG_ERR_INV_ENGINE);
549 if (!notation->value)
551 dest = notation->value = malloc (len);
553 return gpg_error_from_syserror ();
557 int cur_len = strlen (notation->value);
558 dest = realloc (notation->value, len + strlen (notation->value));
560 return gpg_error_from_syserror ();
561 notation->value = dest;
565 err = _gpgme_decode_percent_string (args, &dest, len, 0);
569 notation->value_len += strlen (dest);
572 return gpg_error (GPG_ERR_INV_ENGINE);
578 parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
580 char *end = strchr (args, ' ');
587 case GPGME_STATUS_TRUST_UNDEFINED:
589 sig->validity = GPGME_VALIDITY_UNKNOWN;
592 case GPGME_STATUS_TRUST_NEVER:
593 sig->validity = GPGME_VALIDITY_NEVER;
596 case GPGME_STATUS_TRUST_MARGINAL:
597 sig->validity = GPGME_VALIDITY_MARGINAL;
600 case GPGME_STATUS_TRUST_FULLY:
601 case GPGME_STATUS_TRUST_ULTIMATE:
602 sig->validity = GPGME_VALIDITY_FULL;
606 sig->validity_reason = 0;
607 sig->chain_model = 0;
610 sig->validity_reason = atoi (args);
611 while (*args && *args != ' ')
617 if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2]))
618 sig->chain_model = 1;
626 /* Parse an error status line and if SET_STATUS is true update the
627 result status as appropriate. With SET_STATUS being false, only
628 check for an error. */
630 parse_error (gpgme_signature_t sig, char *args, int set_status)
633 char *where = strchr (args, ' ');
641 where = strchr (which, ' ');
648 return gpg_error (GPG_ERR_INV_ENGINE);
652 if (!strcmp (where, "proc_pkt.plaintext")
653 && gpg_err_code (err) == GPG_ERR_BAD_DATA)
655 /* This indicates a double plaintext. The only solid way to
656 handle this is by failing the oepration. */
657 return gpg_error (GPG_ERR_BAD_DATA);
659 else if (!set_status)
661 else if (!strcmp (where, "verify.findkey"))
663 else if (!strcmp (where, "verify.keyusage")
664 && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
665 sig->wrong_key_usage = 1;
672 _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
674 gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
678 gpgme_signature_t sig;
681 err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
686 sig = opd->current_sig;
690 case GPGME_STATUS_NEWSIG:
692 calc_sig_summary (sig);
693 err = prepare_new_sig (opd);
694 opd->only_newsig_seen = 1;
697 case GPGME_STATUS_GOODSIG:
698 case GPGME_STATUS_EXPSIG:
699 case GPGME_STATUS_EXPKEYSIG:
700 case GPGME_STATUS_BADSIG:
701 case GPGME_STATUS_ERRSIG:
702 case GPGME_STATUS_REVKEYSIG:
703 if (sig && !opd->did_prepare_new_sig)
704 calc_sig_summary (sig);
705 opd->only_newsig_seen = 0;
706 return parse_new_sig (opd, code, args);
708 case GPGME_STATUS_VALIDSIG:
709 opd->only_newsig_seen = 0;
710 return sig ? parse_valid_sig (sig, args)
711 : gpg_error (GPG_ERR_INV_ENGINE);
713 case GPGME_STATUS_NODATA:
714 opd->only_newsig_seen = 0;
716 return gpg_error (GPG_ERR_NO_DATA);
717 sig->status = gpg_error (GPG_ERR_NO_DATA);
720 case GPGME_STATUS_UNEXPECTED:
721 opd->only_newsig_seen = 0;
723 return gpg_error (GPG_ERR_GENERAL);
724 sig->status = gpg_error (GPG_ERR_NO_DATA);
727 case GPGME_STATUS_NOTATION_NAME:
728 case GPGME_STATUS_NOTATION_DATA:
729 case GPGME_STATUS_POLICY_URL:
730 opd->only_newsig_seen = 0;
731 return sig ? parse_notation (sig, code, args)
732 : gpg_error (GPG_ERR_INV_ENGINE);
734 case GPGME_STATUS_TRUST_UNDEFINED:
735 case GPGME_STATUS_TRUST_NEVER:
736 case GPGME_STATUS_TRUST_MARGINAL:
737 case GPGME_STATUS_TRUST_FULLY:
738 case GPGME_STATUS_TRUST_ULTIMATE:
739 opd->only_newsig_seen = 0;
740 return sig ? parse_trust (sig, code, args)
741 : gpg_error (GPG_ERR_INV_ENGINE);
743 case GPGME_STATUS_PKA_TRUST_BAD:
744 case GPGME_STATUS_PKA_TRUST_GOOD:
745 opd->only_newsig_seen = 0;
746 /* Check that we only get one of these status codes per
747 signature; if not the crypto backend misbehaves. */
748 if (!sig || sig->pka_trust || sig->pka_address)
749 return gpg_error (GPG_ERR_INV_ENGINE);
750 sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
751 end = strchr (args, ' ');
754 sig->pka_address = strdup (args);
757 case GPGME_STATUS_ERROR:
758 opd->only_newsig_seen = 0;
759 /* Some error stati are informational, so we don't return an
760 error code if we are not ready to process this status. */
761 return parse_error (sig, args, !!sig );
763 case GPGME_STATUS_EOF:
764 if (sig && !opd->did_prepare_new_sig)
765 calc_sig_summary (sig);
766 if (opd->only_newsig_seen && sig)
768 gpgme_signature_t sig2;
769 /* The last signature has no valid information - remove it
772 if (sig == opd->result.signatures)
773 opd->result.signatures = NULL;
776 for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
777 if (sig2->next == sig)
783 /* Note that there is no need to release the members of SIG
784 because we won't be here if they have been set. */
786 opd->current_sig = NULL;
788 opd->only_newsig_seen = 0;
791 case GPGME_STATUS_PLAINTEXT:
792 if (++opd->plaintext_seen > 1)
793 return gpg_error (GPG_ERR_BAD_DATA);
794 err = _gpgme_parse_plaintext (args, &opd->result.file_name);
806 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
810 err = _gpgme_progress_status_handler (priv, code, args);
812 err = _gpgme_verify_status_handler (priv, code, args);
818 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
823 return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
824 sizeof (*opd), release_op_data);
829 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
830 gpgme_data_t signed_text, gpgme_data_t plaintext)
834 err = _gpgme_op_reset (ctx, synchronous);
838 err = _gpgme_op_verify_init_result (ctx);
842 _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
845 return gpg_error (GPG_ERR_NO_DATA);
846 if (!signed_text && !plaintext)
847 return gpg_error (GPG_ERR_INV_VALUE);
849 return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
853 /* Decrypt ciphertext CIPHER and make a signature verification within
854 CTX and store the resulting plaintext in PLAIN. */
856 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
857 gpgme_data_t signed_text, gpgme_data_t plaintext)
860 TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify_start", ctx,
861 "sig=%p, signed_text=%p, plaintext=%p",
862 sig, signed_text, plaintext);
865 return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
867 err = verify_start (ctx, 0, sig, signed_text, plaintext);
868 return TRACE_ERR (err);
872 /* Decrypt ciphertext CIPHER and make a signature verification within
873 CTX and store the resulting plaintext in PLAIN. */
875 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
876 gpgme_data_t plaintext)
880 TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify", ctx,
881 "sig=%p, signed_text=%p, plaintext=%p",
882 sig, signed_text, plaintext);
885 return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
887 err = verify_start (ctx, 1, sig, signed_text, plaintext);
889 err = _gpgme_wait_one (ctx);
890 return TRACE_ERR (err);
894 /* Compatibility interfaces. */
896 /* Get the key used to create signature IDX in CTX and return it in
899 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
901 gpgme_verify_result_t result;
902 gpgme_signature_t sig;
905 return gpg_error (GPG_ERR_INV_VALUE);
907 result = gpgme_op_verify_result (ctx);
908 sig = result->signatures;
916 return gpg_error (GPG_ERR_EOF);
918 return gpgme_get_key (ctx, sig->fpr, r_key, 0);
922 /* Retrieve the signature status of signature IDX in CTX after a
923 successful verify operation in R_STAT (if non-null). The creation
924 time stamp of the signature is returned in R_CREATED (if non-null).
925 The function returns a string containing the fingerprint. */
927 gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
928 _gpgme_sig_stat_t *r_stat, time_t *r_created)
930 gpgme_verify_result_t result;
931 gpgme_signature_t sig;
933 result = gpgme_op_verify_result (ctx);
934 sig = result->signatures;
946 switch (gpg_err_code (sig->status))
948 case GPG_ERR_NO_ERROR:
949 *r_stat = GPGME_SIG_STAT_GOOD;
952 case GPG_ERR_BAD_SIGNATURE:
953 *r_stat = GPGME_SIG_STAT_BAD;
956 case GPG_ERR_NO_PUBKEY:
957 *r_stat = GPGME_SIG_STAT_NOKEY;
960 case GPG_ERR_NO_DATA:
961 *r_stat = GPGME_SIG_STAT_NOSIG;
964 case GPG_ERR_SIG_EXPIRED:
965 *r_stat = GPGME_SIG_STAT_GOOD_EXP;
968 case GPG_ERR_KEY_EXPIRED:
969 *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
973 *r_stat = GPGME_SIG_STAT_ERROR;
978 *r_created = sig->timestamp;
983 /* Retrieve certain attributes of a signature. IDX is the index
984 number of the signature after a successful verify operation. WHAT
985 is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
986 one. WHATIDX is to be passed as 0 for most attributes . */
988 gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
989 _gpgme_attr_t what, int whatidx)
991 gpgme_verify_result_t result;
992 gpgme_signature_t sig;
994 result = gpgme_op_verify_result (ctx);
995 sig = result->signatures;
1007 case GPGME_ATTR_CREATED:
1008 return sig->timestamp;
1010 case GPGME_ATTR_EXPIRE:
1011 return sig->exp_timestamp;
1013 case GPGME_ATTR_VALIDITY:
1014 return (unsigned long) sig->validity;
1016 case GPGME_ATTR_SIG_STATUS:
1017 switch (gpg_err_code (sig->status))
1019 case GPG_ERR_NO_ERROR:
1020 return GPGME_SIG_STAT_GOOD;
1022 case GPG_ERR_BAD_SIGNATURE:
1023 return GPGME_SIG_STAT_BAD;
1025 case GPG_ERR_NO_PUBKEY:
1026 return GPGME_SIG_STAT_NOKEY;
1028 case GPG_ERR_NO_DATA:
1029 return GPGME_SIG_STAT_NOSIG;
1031 case GPG_ERR_SIG_EXPIRED:
1032 return GPGME_SIG_STAT_GOOD_EXP;
1034 case GPG_ERR_KEY_EXPIRED:
1035 return GPGME_SIG_STAT_GOOD_EXPKEY;
1038 return GPGME_SIG_STAT_ERROR;
1041 case GPGME_ATTR_SIG_SUMMARY:
1042 return sig->summary;
1052 gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
1053 _gpgme_attr_t what, int whatidx)
1055 gpgme_verify_result_t result;
1056 gpgme_signature_t sig;
1058 result = gpgme_op_verify_result (ctx);
1059 sig = result->signatures;
1071 case GPGME_ATTR_FPR:
1074 case GPGME_ATTR_ERRTOK:
1076 return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";