Multiple signatures can now be verified.

This commit is contained in:
Werner Koch 2001-02-12 15:23:29 +00:00
parent 6bbbad60f2
commit 06dae7fcf3
7 changed files with 246 additions and 22 deletions

3
TODO
View File

@ -5,4 +5,7 @@
* Allow to use GTK's main loop instead of the select stuff in * Allow to use GTK's main loop instead of the select stuff in
wait.c 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 * need to close a lot of handles in w32-io.c

View File

@ -1,3 +1,14 @@
2001-02-12 Werner Koch <wk@gnupg.org>
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 <wk@gnupg.org> 2001-02-01 Werner Koch <wk@gnupg.org>
* w32-io.c (set_synchronize): Add EVENT_MODIFY_STATE. Add Debug * w32-io.c (set_synchronize): Add EVENT_MODIFY_STATE. Add Debug

View File

@ -23,6 +23,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h> #include <stdarg.h>
#include <unistd.h>
#include <assert.h> #include <assert.h>
#include "util.h" #include "util.h"

View File

@ -21,6 +21,7 @@
#ifndef GPGME_H #ifndef GPGME_H
#define GPGME_H #define GPGME_H
#include <stdio.h> /* for FILE * */
#ifdef _MSC_VER #ifdef _MSC_VER
typedef long off_t; typedef long off_t;
#else #else
@ -103,7 +104,8 @@ typedef enum {
GPGME_SIG_STAT_BAD = 2, GPGME_SIG_STAT_BAD = 2,
GPGME_SIG_STAT_NOKEY = 3, GPGME_SIG_STAT_NOKEY = 3,
GPGME_SIG_STAT_NOSIG = 4, GPGME_SIG_STAT_NOSIG = 4,
GPGME_SIG_STAT_ERROR = 5 GPGME_SIG_STAT_ERROR = 5,
GPGME_SIG_STAT_DIFF = 6
} GpgmeSigStat; } GpgmeSigStat;
typedef enum { typedef enum {
@ -165,6 +167,12 @@ void gpgme_signers_clear (GpgmeCtx c);
GpgmeError gpgme_signers_add (GpgmeCtx c, const GpgmeKey key); GpgmeError gpgme_signers_add (GpgmeCtx c, const GpgmeKey key);
GpgmeKey gpgme_signers_enum (const GpgmeCtx c, int seq); 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 */ /* Functions to handle recipients */
GpgmeError gpgme_recipients_new (GpgmeRecipients *r_rset); GpgmeError gpgme_recipients_new (GpgmeRecipients *r_rset);

View File

@ -359,7 +359,6 @@ gpgme_op_keylist_start ( GpgmeCtx c, const char *pattern, int secret_only )
_gpgme_release_result (c); _gpgme_release_result (c);
c->out_of_core = 0; c->out_of_core = 0;
#warning This context still keeps a gpg Zombie in some cases.
if ( c->gpg ) { if ( c->gpg ) {
_gpgme_gpg_release ( c->gpg ); _gpgme_gpg_release ( c->gpg );
c->gpg = NULL; c->gpg = NULL;

View File

@ -29,21 +29,28 @@
#include "ops.h" #include "ops.h"
struct verify_result_s { struct verify_result_s {
struct verify_result_s *next;
GpgmeSigStat status; GpgmeSigStat status;
GpgmeData notation; /* we store an XML fragment here */ GpgmeData notation; /* we store an XML fragment here */
int collecting; /* private to finish_sig() */
int notation_in_data; /* private to add_notation() */ 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 void
_gpgme_release_verify_result ( VerifyResult res ) _gpgme_release_verify_result ( VerifyResult res )
{ {
while (res) {
VerifyResult r2 = res->next;
gpgme_data_release ( res->notation ); gpgme_data_release ( res->notation );
xfree (res); xfree (res);
res = r2;
}
} }
/* fixme: check that we are adding this to the correct signature */
static void static void
add_notation ( GpgmeCtx ctx, GpgStatusCode code, const char *data ) 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 static void
verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
{ {
char *p;
int i;
if ( ctx->out_of_core ) if ( ctx->out_of_core )
return; return;
if ( ctx->result_type == RESULT_TYPE_NONE ) { 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 ); assert ( ctx->result_type == RESULT_TYPE_VERIFY );
/* FIXME: For now we handle only one signature */ if (code == STATUS_GOODSIG
/* FIXME: Collect useful information || code == STATUS_BADSIG || code == STATUS_ERRSIG) {
and return them as XML */ finish_sig (ctx,0);
if ( ctx->out_of_core )
return;
}
switch (code) { switch (code) {
case STATUS_GOODSIG: case STATUS_GOODSIG:
ctx->result.verify->status = GPGME_SIG_STAT_GOOD; /* We just look at VALIDSIG */
break; 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: case STATUS_BADSIG:
ctx->result.verify->status = GPGME_SIG_STAT_BAD; 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; break;
case STATUS_ERRSIG: case STATUS_ERRSIG:
ctx->result.verify->status = GPGME_SIG_STAT_ERROR; ctx->result.verify->status = GPGME_SIG_STAT_ERROR;
/* FIXME: distinguish between a regular error and a missing key. /* FIXME: distinguish between a regular error and a missing key.
* this is encoded in the args. */ * 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; break;
case STATUS_NOTATION_NAME: case STATUS_NOTATION_NAME:
@ -127,6 +201,10 @@ verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args )
case STATUS_END_STREAM: case STATUS_END_STREAM:
break; break;
case STATUS_EOF:
finish_sig(ctx,1);
break;
default: default:
/* ignore all other codes */ /* ignore all other codes */
break; 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: * gpgme_op_verify:
* @c: the context * @c: the context
@ -223,7 +316,8 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text )
* missing key * missing key
* GPGME_SIG_STAT_NOSIG: This is not a signature * GPGME_SIG_STAT_NOSIG: This is not a signature
* GPGME_SIG_STAT_ERROR: Due to some other error the check could not be done. * 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 * Return value: 0 on success or an errorcode if something not related to
* the signature itself did go wrong. * 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); rc = mk_error (Out_Of_Core);
else { else {
assert ( c->result.verify ); assert ( c->result.verify );
/* fixme: Put all notation data into one XML fragment */
if ( c->result.verify->notation ) { if ( c->result.verify->notation ) {
GpgmeData dh = 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->notation = dh;
c->result.verify->notation = NULL; c->result.verify->notation = NULL;
} }
*r_stat = c->result.verify->status; *r_stat = intersect_stati (c->result.verify);
} }
c->pending = 0; 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. GPG<ME_EOF is returned to
* indicate that there are no more signatures.
**/
GpgmeError
gpgme_get_sig_key (GpgmeCtx c, int idx, GpgmeKey *r_key)
{
VerifyResult res;
GpgmeCtx listctx;
GpgmeError err;
if (!c || !r_key)
return mk_error (Invalid_Value);
if (c->pending || 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;
}

View File

@ -67,28 +67,59 @@ static const char test_sig1[] =
exit (1); } \ exit (1); } \
} while(0) } while(0)
static void
print_sig_stat ( GpgmeSigStat status ) static const char *
status_string (GpgmeSigStat status)
{ {
const char *s = "?";
switch ( status ) { switch ( status ) {
case GPGME_SIG_STAT_NONE: case GPGME_SIG_STAT_NONE:
fputs ("Verification Status: None\n", stdout); s = "None";
break; break;
case GPGME_SIG_STAT_NOSIG: case GPGME_SIG_STAT_NOSIG:
fputs ("Verification Status: No Signature\n", stdout); s = "No Signature";
break; break;
case GPGME_SIG_STAT_GOOD: case GPGME_SIG_STAT_GOOD:
fputs ("Verification Status: Good\n", stdout); s = "Good";
break; break;
case GPGME_SIG_STAT_BAD: case GPGME_SIG_STAT_BAD:
fputs ("Verification Status: Bad\n", stdout); s = "Bad";
break; break;
case GPGME_SIG_STAT_NOKEY: case GPGME_SIG_STAT_NOKEY:
fputs ("Verification Status: No Key\n", stdout); s = "No Key";
break; break;
case GPGME_SIG_STAT_ERROR: case GPGME_SIG_STAT_ERROR:
fputs ("Verification Status: Error\n", stdout); s = "Error";
break; 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"); puts ("checking a valid message:\n");
err = gpgme_op_verify (ctx, sig, text, &status ); err = gpgme_op_verify (ctx, sig, text, &status );
print_sig_stat ( status ); print_sig_stat ( ctx, status );
fail_if_err (err); fail_if_err (err);
if ( (nota=gpgme_get_notation (ctx)) ) if ( (nota=gpgme_get_notation (ctx)) )
@ -131,7 +162,7 @@ main (int argc, char **argv )
fail_if_err (err); fail_if_err (err);
gpgme_data_rewind ( sig ); gpgme_data_rewind ( sig );
err = gpgme_op_verify (ctx, sig, text, &status ); err = gpgme_op_verify (ctx, sig, text, &status );
print_sig_stat ( status ); print_sig_stat ( ctx, status );
fail_if_err (err); fail_if_err (err);
if ( (nota=gpgme_get_notation (ctx)) ) if ( (nota=gpgme_get_notation (ctx)) )
printf ("---Begin Notation---\n%s---End Notation---\n", nota ); printf ("---Begin Notation---\n%s---End Notation---\n", nota );