Page MenuHome GnuPG

Query which key will be used for a given mailbox
Closed, ResolvedPublic

Description

For a UI it can be useful to check which key gnupg would select for a given mailbox.
And this would ideally be the "best" key to use for this mailbox.

e.g. an API to check which key: gpg -er aheinecke@intevation.de

would use.

Currently:
--locate-keys

Locate the keys given as arguments.  This command basically uses the same

algorithm as used when locating keys for encryption or signing and may thus be
used to see what keys gpg2 might use. In particular external methods as
defined by --auto-key-locate may be used to locate a key. Only public keys are
listed.

Also provides expired keys or keys with unknown validity. Even if a fully valid
key is available.

The result should be a single key or no key.

Details

Version
master

Event Timeline

The algorithm I'm using now to detect the best key from a locate-keys result is
(Q_FOREACH just means "iterate over all elements in this list"):

Key keyC; /* The key candidate */
UserID uidC; /* The uid candidate */
Q_FOREACH (const Key k, keys) {

if (canEncrypt && !k.canEncrypt()) {
    continue;
}
/* First get the uid that matches the mailbox */
Q_FOREACH (const UserID u, k.userIDs()) {
    if (QString::fromUtf8(u.email()).toLower() == mailbox.toLower()) {
        if (uidC.isNull()) {
            keyC = k;
            uidC = u;
        } else if ((!uidIsOk(uidC) && uidIsOk(u)) || uidC.validity() <

u.validity()) {

            /* Validity of the new key is better. */
            uidC = u;
            keyC = k;
        } else if (uidC.validity() == u.validity() && uidIsOk(u)) {
            /* Both are the same check which one is newer. */
            time_t oldTime = 0;
            Q_FOREACH (const Subkey s, keyC.subkeys()) {
                if ((canEncrypt && s.canEncrypt()) && subkeyIsOk(s)) {
                    oldTime = s.creationTime();
                }
            }
            time_t newTime = 0;
            Q_FOREACH (const Subkey s, k.subkeys()) {
                if ((canEncrypt && s.canEncrypt()) && subkeyIsOk(s)) {
                    newTime = s.creationTime();
                }
            }
            if (newTime > oldTime) {
                uidC = u;
                keyC = k;
            }
        }
    }
}

}

The helper functions to check if a key / subkey / uid is ok are just:

static bool keyIsOk(const Key k)
{

return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled();

}

static bool uidIsOk(const UserID uid)
{

return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid();

}

static bool subkeyIsOk(const Subkey s)
{

return !s.isRevoked() && !s.isInvalid() && !s.isDisabled();

}

How do you plan to handle the case that "local" lookup only yields expired or
revoked keys. Will GnuPG then automatically fall back to other locate-keys methods?
This would be my wish so that a MUA / User of that command does not have to care
about that case :-)

To piggyback something on this issue.

To quote T2359 (aheinecke on May 17 2016, 11:59 AM / Roundup):

e.g. an API to check which key: gpg -er aheinecke@intevation.de

I did not have groups on the radar for this. If a recipient is a group then
gnupg would use multiple keys in this command.

I think locate-keys would be a great mechanism to support this easily in MUAs.
When we change it that for a given mailbox only the single most valid Key is
returned we could also have the semantic that if then multiple Keys are returned
we have a group.

werner added a subscriber: justus.

According to T1143 (aheinecke on Jun 08 2016, 07:15 PM / Roundup) the plan is that locate-key as well as -r uses a new
mechanism to figure oiut the appropriate key. aheinecke already implemented
this strategy in Kmail but we want to have it in gnupg proper.

If the given key is specified by a mail address the new scheme kicks in for
--locate-key and all keys given with -r. gpg finds all matching non-expired and
suitable keys and then computes the validity (WoT, TOFU, whatever). That is
list ordered and the top ranked key is used. Newer keys/subkeys are preferred
and thus in general there should never be an ambiguity. In case there is an
ambiguity, -r should return an error and --locate-key should return all those keys.

Please clarify the plan a bit. Shall we use the algorithm currently used by
--recipient, the one used by --locate-key, or implement a new one?

The idea is to change the algorithm in the case that a full mail address is
given - and only a mail address. For both -r and --locate-key. g10/getkey.c
has get_pubkey_byname which implements --locate-key and already checks for a
mail address (IS_MBOX). This function needs to be changed to figure out all
matching keys an return the best one. -r should make use of that function also
if it is a mailbox.

I like this work, thanks for it! I wonder whether it would also be useful for
full-match userID, not only for a raw e-mail address?

For example, if i query for '=Peter Palfrader' or '=ssh://host.example', it
ought to give me the key with the highest-validity binding for the requested
user ID.

Fixed in ab89164be02012f1bf159c971853b8610e966301.

I also don't quite understand why we restrict this to user ids resembling mail
addresses, so I'll keep issue this open for discussion.

For backward compatibily reasons.

That is becuase we consider a mail address to be a unique indentifier and thus
and algorithm to figure out the best matching makes sense. Other kinds of user
IDS not not need to be unique and should at best return an ambigious key error.
Well, expired keys and such should be sorted out, though.