core: Change the way TOFU information are represented.

* src/gpgme.h.in (struct _gpgme_signature): Remove field 'tofu'.  Add
field 'key'.
(struct _gpgme_key): Add field 'fpr'.
(struct _gpgme_user_id): Add field 'tofu'.
(struct _gpgme_tofu_info): Remove fields 'address' and 'fpr'.
* src/key.c (gpgme_key_unref): Release TOFU and FPR.
* src/keylist.c (keylist_colon_handler): Store the fingerprint of the
first subkey also in KEY.
* src/verify.c (release_tofu_info): Remove.
(release_op_data): Release KEY.
(parse_tofu_user): Rewrite for new data structure.
(parse_tofu_stats): Ditto.
(parse_tofu_stats_long): Ditto.
* tests/run-verify.c (print_result): Ditto.
* tests/run-keylist.c (main): Print more fields.
--

TOFU information are now associated with the user ID and not with a
separate object.

Note that this breaks code relying on the former non-released TOFU
feature.  The C++ bindings won't work right now.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-08-23 15:22:28 +02:00
parent 3955dce06e
commit be4ff75d7d
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
9 changed files with 213 additions and 123 deletions

4
NEWS
View File

@ -15,8 +15,10 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_PK_EDDSA NEW. GPGME_PK_EDDSA NEW.
gpgme_set_ctx_flag NEW. gpgme_set_ctx_flag NEW.
gpgme_data_set_flag NEW. gpgme_data_set_flag NEW.
gpgme_signature_t EXTENDED: New field tofu. gpgme_signature_t EXTENDED: New field key.
gpgme_key_t EXTENDED: New field fpr.
gpgme_subkey_t EXTENDED: New field keygrip. gpgme_subkey_t EXTENDED: New field keygrip.
gpgme_user_id_t EXTENDED: New field tofu.
gpgme_tofu_policy_t NEW. gpgme_tofu_policy_t NEW.
gpgme_tofu_info_t NEW. gpgme_tofu_info_t NEW.
GPGME_STATUS_KEY_CONSIDERED NEW. GPGME_STATUS_KEY_CONSIDERED NEW.

View File

@ -3017,6 +3017,10 @@ This is the key ID of the subkey in hexadecimal digits.
This is the fingerprint of the subkey in hexadecimal digits, if This is the fingerprint of the subkey in hexadecimal digits, if
available. available.
@item char *keygrip
The keygrip of the subkey in hex digit form or @code{NULL} if not
availabale.
@item long int timestamp @item long int timestamp
This is the creation timestamp of the subkey. This is -1 if the This is the creation timestamp of the subkey. This is -1 if the
timestamp is invalid, and 0 if it is not available. timestamp is invalid, and 0 if it is not available.
@ -3144,6 +3148,16 @@ This is the comment component of @code{uid}, if available.
@item char *email @item char *email
This is the email component of @code{uid}, if available. This is the email component of @code{uid}, if available.
@item char *address;
The mail address (addr-spec from RFC-5322) of the user ID string.
This is general the same as the @code{email} part of this structure
but might be slightly different. If no mail address is available
@code{NULL} is stored.
@item gpgme_tofu_info_t tofu
If not @code{NULL} information from the TOFU database pertaining to
this user id.
@item gpgme_key_sig_t signatures @item gpgme_key_sig_t signatures
This is a linked list with the signatures on this user ID. This is a linked list with the signatures on this user ID.
@end table @end table
@ -3168,8 +3182,8 @@ This is true if the key is disabled.
@item unsigned int invalid : 1 @item unsigned int invalid : 1
This is true if the key is invalid. This might have several reasons, This is true if the key is invalid. This might have several reasons,
for a example for the S/MIME backend, it will be set in during key for a example for the S/MIME backend, it will be set during key
listsing if the key could not be validated due to a missing listings if the key could not be validated due to missing
certificates or unmatched policies. certificates or unmatched policies.
@item unsigned int can_encrypt : 1 @item unsigned int can_encrypt : 1
@ -3224,6 +3238,13 @@ in the list is the primary key and usually available.
@item gpgme_user_id_t uids @item gpgme_user_id_t uids
This is a linked list with the user IDs of the key. The first user ID This is a linked list with the user IDs of the key. The first user ID
in the list is the main (or primary) user ID. in the list is the main (or primary) user ID.
@item char *fpr
This field gives the fingerprint of the primary key. Note that
this is a copy of the fingerprint of the first subkey. For an
incomplete key (for example from a verification result) a subkey may
be missing but this field may be set nevertheless.
@end table @end table
@end deftp @end deftp
@ -4870,6 +4891,13 @@ The hash algorithm used to create this signature.
@item char *pka_address @item char *pka_address
The mailbox from the PKA information or @code{NULL}. The mailbox from the PKA information or @code{NULL}.
@item gpgme_key_t key
An object describing the key used to create the signature. This key
object may be incomplete in that it only conveys information
availabale directly with a signature. It may also be @code{NULL} if
such information is not readily available.
@end table @end table
@end deftp @end deftp

View File

@ -624,6 +624,41 @@ struct _gpgme_engine_info
typedef struct _gpgme_engine_info *gpgme_engine_info_t; typedef struct _gpgme_engine_info *gpgme_engine_info_t;
/* An object with TOFU information. */
struct _gpgme_tofu_info
{
struct _gpgme_tofu_info *next;
/* The TOFU validity:
* 0 := conflict
* 1 := key without history
* 2 := key with too little history
* 3 := key with enough history for basic trust
* 4 := key with a lot of history
*/
unsigned int validity : 3;
/* The TOFU policy (gpgme_tofu_policy_t). */
unsigned int policy : 4;
unsigned int _rfu : 25;
/* Number of signatures seen for this binding. Capped at USHRT_MAX. */
unsigned short signcount;
/* Number of encryptions done with this binding. Capped at USHRT_MAX. */
unsigned short encrcount;
/* Number of seconds since the first and the most recently seen
* message was verified. */
unsigned int firstseen;
unsigned int lastseen;
/* If non-NULL a human readable string summarizing the TOFU data. */
char *description;
};
typedef struct _gpgme_tofu_info *gpgme_tofu_info_t;
/* A subkey from a key. */ /* A subkey from a key. */
struct _gpgme_subkey struct _gpgme_subkey
{ {
@ -807,6 +842,9 @@ struct _gpgme_user_id
* might be slightly different. IF no mail address is available * might be slightly different. IF no mail address is available
* NULL is stored. */ * NULL is stored. */
char *address; char *address;
/* The malloced tofo information or NULL. */
gpgme_tofu_info_t tofu;
}; };
typedef struct _gpgme_user_id *gpgme_user_id_t; typedef struct _gpgme_user_id *gpgme_user_id_t;
@ -883,6 +921,11 @@ struct _gpgme_key
/* The keylist mode that was active when listing the key. */ /* The keylist mode that was active when listing the key. */
gpgme_keylist_mode_t keylist_mode; gpgme_keylist_mode_t keylist_mode;
/* This field gives the fingerprint of the primary key. Note that
* this is a copy of the FPR of the first subkey. We need it here
* to allow for an incomplete key object. */
char *fpr;
}; };
typedef struct _gpgme_key *gpgme_key_t; typedef struct _gpgme_key *gpgme_key_t;
@ -1570,50 +1613,6 @@ typedef enum
gpgme_sigsum_t; gpgme_sigsum_t;
struct _gpgme_tofu_info
{
struct _gpgme_tofu_info *next;
/* The mail address (addr-spec from RFC5322) of the tofu binding.
*
* If no mail address is set for a User ID this is the name used
* for the user ID. Can be ambiguous when the same mail address or
* name is used in multiple user ids.
*/
char *address;
/* The fingerprint of the primary key. */
char *fpr;
/* The TOFU validity:
* 0 := conflict
* 1 := key without history
* 2 := key with too little history
* 3 := key with enough history for basic trust
* 4 := key with a lot of history
*/
unsigned int validity : 3;
/* The TOFU policy (gpgme_tofu_policy_t). */
unsigned int policy : 4;
unsigned int _rfu : 25;
/* Number of signatures seen for this binding. Capped at USHRT_MAX. */
unsigned short signcount;
unsigned short reserved;
/* Number of seconds since the first and the most recently seen
* message was verified. */
unsigned int firstseen;
unsigned int lastseen;
/* If non-NULL a human readable string summarizing the TOFU data. */
char *description;
};
typedef struct _gpgme_tofu_info *gpgme_tofu_info_t;
struct _gpgme_signature struct _gpgme_signature
{ {
struct _gpgme_signature *next; struct _gpgme_signature *next;
@ -1621,7 +1620,7 @@ struct _gpgme_signature
/* A summary of the signature status. */ /* A summary of the signature status. */
gpgme_sigsum_t summary; gpgme_sigsum_t summary;
/* The fingerprint or key ID of the signature. */ /* The fingerprint of the signature. This can be a subkey. */
char *fpr; char *fpr;
/* The status of the signature. */ /* The status of the signature. */
@ -1660,8 +1659,9 @@ struct _gpgme_signature
/* The mailbox from the PKA information or NULL. */ /* The mailbox from the PKA information or NULL. */
char *pka_address; char *pka_address;
/* If non-NULL, TOFU info for this signature are available. */ /* If non-NULL, a possible incomplete key object with the data
gpgme_tofu_info_t tofu; * available for the signature. */
gpgme_key_t key;
}; };
typedef struct _gpgme_signature *gpgme_signature_t; typedef struct _gpgme_signature *gpgme_signature_t;

View File

@ -356,6 +356,7 @@ gpgme_key_unref (gpgme_key_t key)
{ {
gpgme_user_id_t next_uid = uid->next; gpgme_user_id_t next_uid = uid->next;
gpgme_key_sig_t keysig = uid->signatures; gpgme_key_sig_t keysig = uid->signatures;
gpgme_tofu_info_t tofu = uid->tofu;
while (keysig) while (keysig)
{ {
@ -373,8 +374,21 @@ gpgme_key_unref (gpgme_key_t key)
free (keysig); free (keysig);
keysig = next_keysig; keysig = next_keysig;
} }
while (tofu)
{
/* NB: The ->next is currently not used but we are prepared
* for it. */
gpgme_tofu_info_t tofu_next = tofu->next;
free (tofu->description);
free (tofu);
tofu = tofu_next;
}
if (uid->address && uid->address != uid->email) if (uid->address && uid->address != uid->email)
free (uid->address); free (uid->address);
free (uid); free (uid);
uid = next_uid; uid = next_uid;
} }
@ -386,10 +400,13 @@ gpgme_key_unref (gpgme_key_t key)
if (key->chain_id) if (key->chain_id)
free (key->chain_id); free (key->chain_id);
if (key->fpr)
free (key->fpr);
free (key); free (key);
} }
/* Support functions. */ /* Support functions. */

View File

@ -708,6 +708,22 @@ keylist_colon_handler (void *priv, char *line)
if (!subkey->fpr) if (!subkey->fpr)
return gpg_error_from_syserror (); return gpg_error_from_syserror ();
} }
/* If this is the first subkey, store the fingerprint also
in the KEY object. */
if (subkey == key->subkeys)
{
if (key->fpr && strcmp (key->fpr, subkey->fpr))
{
/* FPR already set but mismatch: Should never happen. */
return trace_gpg_error (GPG_ERR_INTERNAL);
}
if (!key->fpr)
{
key->fpr = strdup (subkey->fpr);
if (!key->fpr)
return gpg_error_from_syserror ();
}
}
} }
/* Field 13 has the gpgsm chain ID (take only the first one). */ /* Field 13 has the gpgsm chain ID (take only the first one). */

View File

@ -138,9 +138,11 @@ gpgme_error_t _gpgme_progress_status_handler (void *priv,
gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key); gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key);
gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key, gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key,
gpgme_subkey_t *r_subkey); gpgme_subkey_t *r_subkey);
gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert); gpgme_error_t _gpgme_key_append_name (gpgme_key_t key,
const char *src, int convert);
gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src); gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src);
/* From keylist.c. */ /* From keylist.c. */
void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type,

View File

@ -49,22 +49,6 @@ typedef struct
} *op_data_t; } *op_data_t;
static void
release_tofu_info (gpgme_tofu_info_t t)
{
while (t)
{
gpgme_tofu_info_t t2 = t->next;
free (t->address);
free (t->fpr);
free (t->description);
free (t);
t = t2;
}
}
static void static void
release_op_data (void *hook) release_op_data (void *hook)
{ {
@ -88,7 +72,8 @@ release_op_data (void *hook)
free (sig->fpr); free (sig->fpr);
if (sig->pka_address) if (sig->pka_address)
free (sig->pka_address); free (sig->pka_address);
release_tofu_info (sig->tofu); if (sig->key)
gpgme_key_unref (sig->key);
free (sig); free (sig);
sig = next; sig = next;
} }
@ -690,49 +675,80 @@ parse_tofu_user (gpgme_signature_t sig, char *args)
{ {
gpg_error_t err; gpg_error_t err;
char *tail; char *tail;
gpgme_tofu_info_t ti, ti2; gpgme_user_id_t uid;
gpgme_tofu_info_t ti;
char *fpr = NULL;
char *address = NULL;
tail = strchr (args, ' '); tail = strchr (args, ' ');
if (!tail || tail == args) if (!tail || tail == args)
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */ {
err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No fingerprint. */
goto leave;
}
*tail++ = 0; *tail++ = 0;
ti = calloc (1, sizeof *ti); fpr = strdup (args);
if (!ti) if (!fpr)
return gpg_error_from_syserror ();
ti->fpr = strdup (args);
if (!ti->fpr)
{ {
free (ti); err = gpg_error_from_syserror ();
return gpg_error_from_syserror (); goto leave;
} }
args = tail; args = tail;
tail = strchr (args, ' '); tail = strchr (args, ' ');
if (tail == args) if (tail == args)
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */ {
err = trace_gpg_error (GPG_ERR_INV_ENGINE); /* No addr-spec. */
goto leave;
}
if (tail) if (tail)
*tail = 0; *tail = 0;
err = _gpgme_decode_percent_string (args, &ti->address, 0, 0); err = _gpgme_decode_percent_string (args, &address, 0, 0);
if (err) if (err)
goto leave;
if (!sig->key)
{ {
free (ti); err = _gpgme_key_new (&sig->key);
return err; if (err)
goto leave;
sig->key->fpr = fpr;
fpr = NULL;
}
else if (!sig->key->fpr)
{
err = trace_gpg_error (GPG_ERR_INTERNAL);
goto leave;
}
else if (strcmp (sig->key->fpr, fpr))
{
/* The engine did not emit NEWSIG before a new key. */
err = trace_gpg_error (GPG_ERR_INV_ENGINE);
goto leave;
} }
/* Append to the tofu info list. */ err = _gpgme_key_append_name (sig->key, address, 0);
if (!sig->tofu) if (err)
sig->tofu = ti; goto leave;
else
{
for (ti2 = sig->tofu; ti2->next; ti2 = ti2->next)
;
ti2->next = ti;
}
return 0; uid = sig->key->_last_uid;
assert (uid);
ti = calloc (1, sizeof *ti);
if (!ti)
{
err = gpg_error_from_syserror ();
goto leave;
}
uid->tofu = ti;
leave:
free (fpr);
free (address);
return err;
} }
@ -749,12 +765,10 @@ parse_tofu_stats (gpgme_signature_t sig, char *args)
int nfields; int nfields;
unsigned long uval; unsigned long uval;
if (!sig->tofu) if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
for (ti = sig->tofu; ti->next; ti = ti->next)
;
if (ti->firstseen || ti->signcount || ti->validity || ti->policy) if (ti->firstseen || ti->signcount || ti->validity || ti->policy)
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */
nfields = _gpgme_split_fields (args, field, DIM (field)); nfields = _gpgme_split_fields (args, field, DIM (field));
if (nfields < 3) if (nfields < 3)
@ -825,12 +839,10 @@ parse_tofu_stats_long (gpgme_signature_t sig, char *args, int raw)
gpgme_tofu_info_t ti; gpgme_tofu_info_t ti;
char *p; char *p;
if (!sig->tofu) if (!sig->key || !sig->key->_last_uid || !(ti = sig->key->_last_uid->tofu))
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* No TOFU_USER seen. */
for (ti = sig->tofu; ti->next; ti = ti->next)
;
if (ti->description) if (ti->description)
return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already seen. */ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Already set. */
err = _gpgme_decode_percent_string (args, &ti->description, 0, 0); err = _gpgme_decode_percent_string (args, &ti->description, 0, 0);
if (err) if (err)

View File

@ -233,7 +233,14 @@ main (int argc, char **argv)
for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++) for (nuids=0, uid=key->uids; uid; uid = uid->next, nuids++)
{ {
printf ("userid %d: %s\n", nuids, nonnull(uid->uid)); printf ("userid %d: %s\n", nuids, nonnull(uid->uid));
printf ("valid %d: %s\n", nuids, printf (" mbox %d: %s\n", nuids, nonnull(uid->address));
if (uid->email && uid->email != uid->address)
printf (" email %d: %s\n", nuids, uid->email);
if (uid->name)
printf (" name %d: %s\n", nuids, uid->name);
if (uid->comment)
printf (" cmmnt %d: %s\n", nuids, uid->comment);
printf (" valid %d: %s\n", nuids,
uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown": uid->validity == GPGME_VALIDITY_UNKNOWN? "unknown":
uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined": uid->validity == GPGME_VALIDITY_UNDEFINED? "undefined":
uid->validity == GPGME_VALIDITY_NEVER? "never": uid->validity == GPGME_VALIDITY_NEVER? "never":

View File

@ -111,6 +111,7 @@ print_result (gpgme_verify_result_t result)
{ {
gpgme_signature_t sig; gpgme_signature_t sig;
gpgme_sig_notation_t nt; gpgme_sig_notation_t nt;
gpgme_user_id_t uid;
gpgme_tofu_info_t ti; gpgme_tofu_info_t ti;
int count = 0; int count = 0;
@ -153,29 +154,34 @@ print_result (gpgme_verify_result_t result)
if ((nt->value?strlen (nt->value):0) != nt->value_len) if ((nt->value?strlen (nt->value):0) != nt->value_len)
printf (" warning : value larger (%d)\n", nt->value_len); printf (" warning : value larger (%d)\n", nt->value_len);
} }
for (ti = sig->tofu; ti; ti = ti->next) if (sig->key)
{ {
printf (" tofu addr .: %s\n", ti->address); printf (" primary fpr: %s\n", nonnull (sig->key->fpr));
if (!sig->fpr || strcmp (sig->fpr, ti->fpr)) for (uid = sig->key->uids; uid; uid = uid->next)
printf (" WARNING .: fpr mismatch (%s)\n", ti->fpr); {
printf (" validity : %u (%s)\n", ti->validity, printf (" tofu addr .: %s\n", nonnull (uid->address));
ti->validity == 0? "conflict" : ti = uid->tofu;
ti->validity == 1? "no history" : if (!ti)
ti->validity == 2? "little history" : continue;
ti->validity == 3? "enough history" : printf (" validity : %u (%s)\n", ti->validity,
ti->validity == 4? "lot of history" : "?"); ti->validity == 0? "conflict" :
printf (" policy ..: %u (%s)\n", ti->policy, ti->validity == 1? "no history" :
ti->policy == GPGME_TOFU_POLICY_NONE? "none" : ti->validity == 2? "little history" :
ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" : ti->validity == 3? "enough history" :
ti->policy == GPGME_TOFU_POLICY_GOOD? "good" : ti->validity == 4? "lot of history" : "?");
ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" : printf (" policy ..: %u (%s)\n", ti->policy,
ti->policy == GPGME_TOFU_POLICY_BAD? "bad" : ti->policy == GPGME_TOFU_POLICY_NONE? "none" :
ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?"); ti->policy == GPGME_TOFU_POLICY_AUTO? "auto" :
printf (" sigcount : %hu\n", ti->signcount); ti->policy == GPGME_TOFU_POLICY_GOOD? "good" :
printf (" firstseen: %u\n", ti->firstseen); ti->policy == GPGME_TOFU_POLICY_UNKNOWN? "unknown" :
printf (" lastseen : %u\n", ti->lastseen); ti->policy == GPGME_TOFU_POLICY_BAD? "bad" :
printf (" desc ....: "); ti->policy == GPGME_TOFU_POLICY_ASK? "ask" : "?");
print_description (nonnull (ti->description), 15); printf (" sigcount : %hu\n", ti->signcount);
printf (" firstseen: %u\n", ti->firstseen);
printf (" lastseen : %u\n", ti->lastseen);
printf (" desc ....: ");
print_description (nonnull (ti->description), 15);
}
} }
} }
} }