Allow fingerprint user input for key selection. (see #3695) Accept and check input of a fingerprint and find the matching key.
Note that for both to work, match against and display of fingerprint, the pgp_list_pubring_command and pgp_list_secring_command need to contain the --with-fingerprint option, or have with-fingerprint in ~/.gnupg/gpg.conf. -Kevin
# HG changeset patch # User Eike Rathke <er...@erack.de> # Date 1423687117 -3600 # Wed Feb 11 21:38:37 2015 +0100 # Node ID f5b1b75c595809ad658a11898ddaf8aca850c484 # Parent af5951f5d81c13b54f8896b2b372fb1774547c06 Allow fingerprint user input for key selection. (see #3695) Accept and check input of a fingerprint and find the matching key. Note that for both to work, match against and display of fingerprint, the pgp_list_pubring_command and pgp_list_secring_command need to contain the --with-fingerprint option, or have with-fingerprint in ~/.gnupg/gpg.conf. diff --git a/crypt-gpgme.c b/crypt-gpgme.c --- a/crypt-gpgme.c +++ b/crypt-gpgme.c @@ -4183,64 +4183,57 @@ static crypt_key_t *crypt_getkeybystr (char *p, short abilities, unsigned int app, int *forced_valid) { LIST *hints = NULL; crypt_key_t *keys; crypt_key_t *matches = NULL; crypt_key_t **matches_endp = &matches; crypt_key_t *k; - const char *ps, *pl; + const char *ps, *pl, *pfcopy, *phint; mutt_message (_("Looking for keys matching \"%s\"..."), p); *forced_valid = 0; - hints = crypt_add_string_to_hints (hints, p); + pfcopy = crypt_get_fingerprint_or_id (p, &phint, &pl, &ps); + hints = crypt_add_string_to_hints (hints, phint); keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN)); mutt_free_list (&hints); if (!keys) + { + FREE (&pfcopy); return NULL; - - /* User input may be short or long key ID, independent of OPTPGPLONGIDS. - * crypt_key_t->keyid should always contain a long key ID without 0x. - * Strip leading "0x" before loops so it doesn't have to be done over and - * over again, and prepare pl and ps to simplify logic in the loop's inner - * condition. - */ - pl = (!mutt_strncasecmp (p, "0x", 2) ? p + 2 : p); - ps = (mutt_strlen (pl) == 16 ? pl + 8 : pl); - - /* If ps != pl it means a long ID (or name of 16 characters) was given, do - * not attempt to match short IDs then. Also, it is unnecessary to try to - * match pl against long IDs if ps == pl as pl could not be a long ID. */ - + } + for (k = keys; k; k = k->next) { if (abilities && !(k->flags & abilities)) continue; dprint (5, (debugfile, "crypt_getkeybystr: matching \"%s\" against " "key %s, \"%s\": ", p, crypt_long_keyid (k), k->uid)); if (!*p - || (ps != pl && mutt_strcasecmp (pl, crypt_long_keyid (k)) == 0) - || (ps == pl && mutt_strcasecmp (ps, crypt_short_keyid (k)) == 0) + || (pfcopy && mutt_strcasecmp (pfcopy, crypt_fpr (k)) == 0) + || (pl && mutt_strcasecmp (pl, crypt_long_keyid (k)) == 0) + || (ps && mutt_strcasecmp (ps, crypt_short_keyid (k)) == 0) || mutt_stristr (k->uid, p)) { crypt_key_t *tmp; dprint (5, (debugfile, "match.\n")); *matches_endp = tmp = crypt_copy_key (k); matches_endp = &tmp->next; } } + FREE (&pfcopy); crypt_free_key (&keys); if (matches) { k = crypt_select_key (matches, NULL, p, app, forced_valid); crypt_free_key (&matches); return k; } diff --git a/crypt.c b/crypt.c --- a/crypt.c +++ b/crypt.c @@ -895,8 +895,88 @@ if (s->flags & M_DISPLAY && sigcnt) state_attach_puts (_("\n[-- End of signed data --]\n"), s); return rc; } +/* Obtain pointers to fingerprint or short or long key ID, if any. + * See mutt_crypt.h for details. + */ +const char* crypt_get_fingerprint_or_id (char *p, const char **pphint, + const char **ppl, const char **pps) +{ + const char *ps, *pl, *phint; + char *pfcopy, *pf, *s1, *s2; + char c; + int isid; + size_t hexdigits; + + /* User input may be partial name, fingerprint or short or long key ID, + * independent of OPTPGPLONGIDS. + * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5). + * Strip leading "0x" for key ID detection and prepare pl and ps to indicate + * if an ID was found and to simplify logic in the key loop's inner + * condition of the caller. */ + + pf = mutt_skip_whitespace (p); + if (!mutt_strncasecmp (pf, "0x", 2)) + pf += 2; + + /* Check if a fingerprint is given, must be hex digits only, blanks + * separating groups of 4 hex digits are allowed. Also pre-check for ID. */ + isid = 2; /* unknown */ + hexdigits = 0; + s1 = pf; + do + { + c = *(s1++); + if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')) + { + ++hexdigits; + if (isid == 2) + isid = 1; /* it is an ID so far */ + } + else if (c) + { + isid = 0; /* not an ID */ + if (c == ' ' && ((hexdigits % 4) == 0)) + ; /* skip blank before or after 4 hex digits */ + else + break; /* any other character or position */ + } + } while (c); + + /* If at end of input, check for correct fingerprint length and copy if. */ + pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? safe_strdup (pf) : NULL); + + if (pfcopy) + { + /* Use pfcopy to strip all spaces from fingerprint and as hint. */ + s1 = s2 = pfcopy; + do + { + *(s1++) = *(s2 = mutt_skip_whitespace (s2)); + } while (*(s2++)); + + phint = pfcopy; + ps = pl = NULL; + } + else + { + phint = p; + ps = pl = NULL; + if (isid == 1) + { + if (mutt_strlen (pf) == 16) + pl = pf; /* long key ID */ + else if (mutt_strlen (pf) == 8) + ps = pf; /* short key ID */ + } + } + + *pphint = phint; + *ppl = pl; + *pps = ps; + return pfcopy; +} diff --git a/mutt_crypt.h b/mutt_crypt.h --- a/mutt_crypt.h +++ b/mutt_crypt.h @@ -148,16 +148,30 @@ /* Check that we have a usable passphrase, ask if not. */ int crypt_valid_passphrase (int); /* Write the message body/part A described by state S to a the given TEMPFILE. */ int crypt_write_signed(BODY *a, STATE *s, const char *tempf); +/* Obtain pointers to fingerprint or short or long key ID, if any. + + Upon return, at most one of return, *ppl and *pps pointers is non-NULL, + indicating the longest fingerprint or ID found, if any. + + Return: Copy of fingerprint, if any, stripped of all spaces, else NULL. + Must be FREE'd by caller. + *pphint Start of string to be passed to pgp_add_string_to_hints() or + crypt_add_string_to_hints(). + *ppl Start of long key ID if detected, else NULL. + *pps Start of short key ID if detected, else NULL. */ +const char* crypt_get_fingerprint_or_id (char *p, const char **pphint, + const char **ppl, const char **pps); + /*-- cryptglue.c --*/ /* Show a message that a backend will be invoked. */ void crypt_invoke_message (int type); diff --git a/pgpkey.c b/pgpkey.c --- a/pgpkey.c +++ b/pgpkey.c @@ -927,39 +927,31 @@ LIST *hints = NULL; pgp_key_t keys; pgp_key_t matches = NULL; pgp_key_t *last = &matches; pgp_key_t k, kn; pgp_uid_t *a; short match; size_t l; - const char *ps, *pl; + const char *ps, *pl, *pfcopy, *phint; if ((l = mutt_strlen (p)) && p[l-1] == '!') p[l-1] = 0; mutt_message (_("Looking for keys matching \"%s\"..."), p); - hints = pgp_add_string_to_hints (hints, p); + pfcopy = crypt_get_fingerprint_or_id (p, &phint, &pl, &ps); + hints = pgp_add_string_to_hints (hints, phint); keys = pgp_get_candidates (keyring, hints); mutt_free_list (&hints); if (!keys) goto out; - /* User input may be short or long key ID, independent of OPTPGPLONGIDS. - * pgp_key_t->keyid should always contain a long key ID without 0x. - * Strip leading "0x" before loops so it doesn't have to be done over and - * over again, and prepare pl and ps to simplify logic in the loop's inner - * condition. - */ - pl = (!mutt_strncasecmp (p, "0x", 2) ? p + 2 : p); - ps = (mutt_strlen (pl) == 16 ? pl + 8 : pl); - for (k = keys; k; k = kn) { kn = k->next; if (abilities && !(k->flags & abilities)) continue; /* This shouldn't happen, but keys without any addresses aren't selectable * in pgp_select_key(). @@ -967,22 +959,20 @@ if (!k->address) continue; match = 0; dprint (5, (debugfile, "pgp_getkeybystr: matching \"%s\" against key %s:\n", p, pgp_long_keyid (k))); - /* If ps != pl it means a long ID (or name of 16 characters) was given, do - * not attempt to match short IDs then. Also, it is unnecessary to try to - * match pl against long IDs if ps == pl as pl could not be a long ID. */ if (!*p || - (ps != pl && mutt_strcasecmp (pl, pgp_long_keyid (k)) == 0) || - (ps == pl && mutt_strcasecmp (ps, pgp_short_keyid (k)) == 0)) + (pfcopy && mutt_strcasecmp (pfcopy, k->fingerprint) == 0) || + (pl && mutt_strcasecmp (pl, pgp_long_keyid (k)) == 0) || + (ps && mutt_strcasecmp (ps, pgp_short_keyid (k)) == 0)) { dprint (5, (debugfile, "\t\tmatch.\n")); match = 1; } else { for (a = k->address; a; a = a->next) { @@ -1008,20 +998,22 @@ pgp_free_key (&keys); if (matches) { if ((k = pgp_select_key (matches, NULL, p))) pgp_remove_key (&matches, k); pgp_free_key (&matches); + FREE (&pfcopy); if (l && !p[l-1]) p[l-1] = '!'; return k; } out: + FREE (&pfcopy); if (l && !p[l-1]) p[l-1] = '!'; return NULL; } #endif /* CRYPT_BACKEND_CLASSIC_PGP */
signature.asc
Description: PGP signature