From 06dae7fcf3dd7c4209b52520d5c14d04b1ff3214 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 12 Feb 2001 15:23:29 +0000 Subject: [PATCH] Multiple signatures can now be verified. --- TODO | 3 + gpgme/ChangeLog | 11 +++ gpgme/debug.c | 1 + gpgme/gpgme.h | 10 ++- gpgme/keylist.c | 1 - gpgme/verify.c | 191 ++++++++++++++++++++++++++++++++++++++++++++--- tests/t-verify.c | 51 ++++++++++--- 7 files changed, 246 insertions(+), 22 deletions(-) diff --git a/TODO b/TODO index c6f65057..1ef478da 100644 --- a/TODO +++ b/TODO @@ -5,4 +5,7 @@ * Allow to use GTK's main loop instead of the select stuff in wait.c +* a op_keylist_start should cancel a pending keylisy operation on the + same context + * need to close a lot of handles in w32-io.c diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 6b63ca21..ea4ac7eb 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,14 @@ +2001-02-12 Werner Koch + + Enhanced the signature verification, so that it can how handle + more than one signature and is able to return more information on + the signatures. + * verify.c (gpgme_get_sig_key): New. + (gpgme_get_sig_status): New. + + * gpgme.h: Add stdio.h. + (GpgmeSigStat): New status DIFF. + 2001-02-01 Werner Koch * w32-io.c (set_synchronize): Add EVENT_MODIFY_STATE. Add Debug diff --git a/gpgme/debug.c b/gpgme/debug.c index e700405b..19de2a7d 100644 --- a/gpgme/debug.c +++ b/gpgme/debug.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "util.h" diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 73744922..cecb912c 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -21,6 +21,7 @@ #ifndef GPGME_H #define GPGME_H +#include /* for FILE * */ #ifdef _MSC_VER typedef long off_t; #else @@ -103,7 +104,8 @@ typedef enum { GPGME_SIG_STAT_BAD = 2, GPGME_SIG_STAT_NOKEY = 3, GPGME_SIG_STAT_NOSIG = 4, - GPGME_SIG_STAT_ERROR = 5 + GPGME_SIG_STAT_ERROR = 5, + GPGME_SIG_STAT_DIFF = 6 } GpgmeSigStat; typedef enum { @@ -165,6 +167,12 @@ void gpgme_signers_clear (GpgmeCtx c); GpgmeError gpgme_signers_add (GpgmeCtx c, const GpgmeKey key); GpgmeKey gpgme_signers_enum (const GpgmeCtx c, int seq); +const char *gpgme_get_sig_status (GpgmeCtx c, int idx, + GpgmeSigStat *r_stat, time_t *r_created ); +GpgmeError gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key); + + + /* Functions to handle recipients */ GpgmeError gpgme_recipients_new (GpgmeRecipients *r_rset); diff --git a/gpgme/keylist.c b/gpgme/keylist.c index 9fea4257..8b360e00 100644 --- a/gpgme/keylist.c +++ b/gpgme/keylist.c @@ -359,7 +359,6 @@ gpgme_op_keylist_start ( GpgmeCtx c, const char *pattern, int secret_only ) _gpgme_release_result (c); c->out_of_core = 0; -#warning This context still keeps a gpg Zombie in some cases. if ( c->gpg ) { _gpgme_gpg_release ( c->gpg ); c->gpg = NULL; diff --git a/gpgme/verify.c b/gpgme/verify.c index cc203959..7a1af985 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -29,21 +29,28 @@ #include "ops.h" struct verify_result_s { + struct verify_result_s *next; GpgmeSigStat status; GpgmeData notation; /* we store an XML fragment here */ - + int collecting; /* private to finish_sig() */ int notation_in_data; /* private to add_notation() */ + char fpr[41]; /* fingerprint of a good signature or keyid of a bad one*/ + ulong timestamp; /* signature creation time */ }; void _gpgme_release_verify_result ( VerifyResult res ) { - gpgme_data_release ( res->notation ); - xfree (res); + while (res) { + VerifyResult r2 = res->next; + gpgme_data_release ( res->notation ); + xfree (res); + res = r2; + } } - +/* fixme: check that we are adding this to the correct signature */ static void add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data ) { @@ -86,9 +93,42 @@ add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data ) } } + +/* + * finish a pending signature info collection and prepare for a new + * signature info collection + */ +static void +finish_sig (GpgmeCtx ctx, int stop) +{ + if (stop) + return; /* nothing to do */ + + if (ctx->result.verify->collecting) { + VerifyResult res2; + + ctx->result.verify->collecting = 0; + /* create a new result structure */ + res2 = xtrycalloc ( 1, sizeof *res2 ); + if ( !res2 ) { + ctx->out_of_core = 1; + return; + } + + res2->next = ctx->result.verify; + ctx->result.verify = res2; + } + + ctx->result.verify->collecting = 1; +} + + static void verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) { + char *p; + int i; + if ( ctx->out_of_core ) return; if ( ctx->result_type == RESULT_TYPE_NONE ) { @@ -102,20 +142,54 @@ verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) } assert ( ctx->result_type == RESULT_TYPE_VERIFY ); - /* FIXME: For now we handle only one signature */ - /* FIXME: Collect useful information - and return them as XML */ + if (code == STATUS_GOODSIG + || code == STATUS_BADSIG || code == STATUS_ERRSIG) { + finish_sig (ctx,0); + if ( ctx->out_of_core ) + return; + } + switch (code) { case STATUS_GOODSIG: - ctx->result.verify->status = GPGME_SIG_STAT_GOOD; + /* We just look at VALIDSIG */ break; + + case STATUS_VALIDSIG: + ctx->result.verify->status = GPGME_SIG_STAT_GOOD; + p = ctx->result.verify->fpr; + for (i=0; i < DIM(ctx->result.verify->fpr) + && args[i] && args[i] != ' ' ; i++ ) + *p++ = args[i]; + *p = 0; + /* skip the formatted date */ + while ( args[i] && args[i] == ' ') + i++; + while ( args[i] && args[i] != ' ') + i++; + /* and get the timestamp */ + ctx->result.verify->timestamp = strtoul (args+i, NULL, 10); + break; + case STATUS_BADSIG: ctx->result.verify->status = GPGME_SIG_STAT_BAD; + /* store the keyID in the fpr field */ + p = ctx->result.verify->fpr; + for (i=0; i < DIM(ctx->result.verify->fpr) + && args[i] && args[i] != ' ' ; i++ ) + *p++ = args[i]; + *p = 0; break; + case STATUS_ERRSIG: ctx->result.verify->status = GPGME_SIG_STAT_ERROR; /* FIXME: distinguish between a regular error and a missing key. * this is encoded in the args. */ + /* store the keyID in the fpr field */ + p = ctx->result.verify->fpr; + for (i=0; i < DIM(ctx->result.verify->fpr) + && args[i] && args[i] != ' ' ; i++ ) + *p++ = args[i]; + *p = 0; break; case STATUS_NOTATION_NAME: @@ -127,6 +201,10 @@ verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) case STATUS_END_STREAM: break; + case STATUS_EOF: + finish_sig(ctx,1); + break; + default: /* ignore all other codes */ break; @@ -205,6 +283,21 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) } +/* + * Figure out a common status value for all signatures + */ +static GpgmeSigStat +intersect_stati ( VerifyResult res ) +{ + GpgmeSigStat status = res->status; + + for (res=res->next; res; res = res->next) { + if (status != res->status ) + return GPGME_SIG_STAT_DIFF; + } + return status; +} + /** * gpgme_op_verify: * @c: the context @@ -223,7 +316,8 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) * missing key * GPGME_SIG_STAT_NOSIG: This is not a signature * GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done. - * FIXME: What do we return if only some o the signatures ae valid? + * GPGME_SIG_STAT_DIFF: There is more than 1 signature and they have not + * the same status. * * Return value: 0 on success or an errorcode if something not related to * the signature itself did go wrong. @@ -250,6 +344,7 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text, rc = mk_error (Out_Of_Core); else { assert ( c->result.verify ); + /* fixme: Put all notation data into one XML fragment */ if ( c->result.verify->notation ) { GpgmeData dh = c->result.verify->notation; @@ -261,7 +356,7 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text, c->notation = dh; c->result.verify->notation = NULL; } - *r_stat = c->result.verify->status; + *r_stat = intersect_stati (c->result.verify); } c->pending = 0; } @@ -269,6 +364,82 @@ gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text, } +/** + * gpgme_get_sig_status: + * @c: Context + * @idx: Index of the signature starting at 0 + * @r_stat: Returns the status + * @r_created: Returns the creation timestamp + * + * Return information about an already verified signatures. + * + * Return value: The fingerprint or NULL in case of an problem or + * when there are no more signatures. + **/ +const char * +gpgme_get_sig_status (GpgmeCtx c, int idx, + GpgmeSigStat *r_stat, time_t *r_created ) +{ + VerifyResult res; + + if (!c || c->pending || c->result_type != RESULT_TYPE_VERIFY ) + return NULL; /* No results yet or verification error */ + + for (res = c->result.verify; res && idx>0 ; res = res->next, idx--) + ; + if (!res) + return NULL; /* No more signatures */ + + if (r_stat) + *r_stat = res->status; + if (r_created) + *r_created = res->timestamp; + return res->fpr; +} + + +/** + * gpgme_get_sig_key: + * @c: context + * @idx: Index of the signature starting at 0 + * @r_key: Returns the key object + * + * Return a key object which was used to check the signature. + * + * Return value: An Errorcode or 0 for success. GPGpending || c->result_type != RESULT_TYPE_VERIFY ) + return mk_error (Busy); + + for (res = c->result.verify; res && idx>0 ; res = res->next, idx--) + ; + if (!res) + return mk_error (EOF); + + if (strlen(res->fpr) < 16) /* we have at least an key ID */ + return mk_error (Invalid_Key); + + /* Fixme: This can me optimized keeping + * an internal context used for such key listings */ + if ( (err=gpgme_new (&listctx)) ) + return err; + if ( !(err=gpgme_op_keylist_start (listctx, res->fpr, 0 )) ) + err=gpgme_op_keylist_next ( listctx, r_key ); + gpgme_release (listctx); + + return err; +} + diff --git a/tests/t-verify.c b/tests/t-verify.c index 2b91a99a..22ff1589 100644 --- a/tests/t-verify.c +++ b/tests/t-verify.c @@ -67,28 +67,59 @@ static const char test_sig1[] = exit (1); } \ } while(0) -static void -print_sig_stat ( GpgmeSigStat status ) + +static const char * +status_string (GpgmeSigStat status) { + const char *s = "?"; + switch ( status ) { case GPGME_SIG_STAT_NONE: - fputs ("Verification Status: None\n", stdout); + s = "None"; break; case GPGME_SIG_STAT_NOSIG: - fputs ("Verification Status: No Signature\n", stdout); + s = "No Signature"; break; case GPGME_SIG_STAT_GOOD: - fputs ("Verification Status: Good\n", stdout); + s = "Good"; break; case GPGME_SIG_STAT_BAD: - fputs ("Verification Status: Bad\n", stdout); + s = "Bad"; break; case GPGME_SIG_STAT_NOKEY: - fputs ("Verification Status: No Key\n", stdout); + s = "No Key"; break; case GPGME_SIG_STAT_ERROR: - fputs ("Verification Status: Error\n", stdout); + s = "Error"; break; + case GPGME_SIG_STAT_DIFF: + s = "More than one signature"; + break; + } + return s; +} + + +static void +print_sig_stat ( GpgmeCtx ctx, GpgmeSigStat status ) +{ + const char *s; + time_t created; + int idx; + GpgmeKey key; + + printf ("Verification Status: %s\n", status_string (status)); + + for(idx=0; (s=gpgme_get_sig_status (ctx, idx, &status, &created)); idx++ ) { + printf ("sig %d: created: %lu status: %s\n", idx, (ulong)created, + status_string(status) ); + printf ("sig %d: fpr/keyid=`%s'\n", idx, s ); + if ( !gpgme_get_sig_key (ctx, idx, &key) ) { + char *p = gpgme_key_get_as_xml ( key ); + printf ("sig %d: key object:\n%s\n", idx, p ); + free (p); + gpgme_key_release (key); + } } } @@ -118,7 +149,7 @@ main (int argc, char **argv ) puts ("checking a valid message:\n"); err = gpgme_op_verify (ctx, sig, text, &status ); - print_sig_stat ( status ); + print_sig_stat ( ctx, status ); fail_if_err (err); if ( (nota=gpgme_get_notation (ctx)) ) @@ -131,7 +162,7 @@ main (int argc, char **argv ) fail_if_err (err); gpgme_data_rewind ( sig ); err = gpgme_op_verify (ctx, sig, text, &status ); - print_sig_stat ( status ); + print_sig_stat ( ctx, status ); fail_if_err (err); if ( (nota=gpgme_get_notation (ctx)) ) printf ("---Begin Notation---\n%s---End Notation---\n", nota );