Kevin J. McCarthy wrote: > The interface issues can be discussed and implemented later. The > purpose of this patchset is simply to allow the user to enter a > fingerprint when searching for a key. > > Putting it in a pgp_uid_t is the easiest approach, but it's not the > correct approach. I'd really rather have it done right the first time.
This was a little more involved than just changing the fingerprint field to an array of length 41, so I thought I'd step up and do what I was asking for. Attached are three patches. The first changes the fingerprint to "char *" and modifies pgppubring.c as needed. The second adds fpr parsing to get_candidates() and stores it in the new fingerprint field in the key. The last patch is a folded version of your two patches, slightly cleaned up with the pgp_uid_t stuff removed. (I didn't touch the crypt_get_fingerprint_or_id() function). If you are okay with my modifications to your patches, I'll push these after some more testing and giving others a chance to review them. -Kevin
# HG changeset patch # User Kevin McCarthy <ke...@8t8.us> # Date 1423882226 28800 # Fri Feb 13 18:50:26 2015 -0800 # Node ID 92c80d8bb14487e155a51b5e18646a655c506c28 # Parent 385d7434c9d6f44c732fd12fc76d543f9d5d7233 Convert pgp_key_t fingerprint to a char* (see #3695) Currently only pgppubring.c is using the fingerprint field, and is using it to store a binary version of the fingerprint. Convert the field to store a null-terminated string. Modify pgppubring.c to use to use the new field. diff --git a/pgplib.c b/pgplib.c --- a/pgplib.c +++ b/pgplib.c @@ -178,16 +178,17 @@ if (!kpp || !*kpp) return; kp = *kpp; pgp_free_uid (&kp->address); FREE (&kp->keyid); + FREE (&kp->fingerprint); /* mutt_crypt.h: 'typedef struct pgp_keyinfo *pgp_key_t;' */ FREE (kpp); /* __FREE_CHECKED__ */ } pgp_key_t pgp_remove_key (pgp_key_t *klist, pgp_key_t key) { pgp_key_t *last; pgp_key_t p, q, r; diff --git a/pgplib.h b/pgplib.h --- a/pgplib.h +++ b/pgplib.h @@ -29,32 +29,26 @@ unsigned long sid1; unsigned long sid2; } pgp_sig_t; struct pgp_keyinfo { char *keyid; + char *fingerprint; struct pgp_uid *address; int flags; short keylen; time_t gen_time; int numalg; const char *algorithm; struct pgp_keyinfo *parent; struct pgp_signature *sigs; struct pgp_keyinfo *next; - - short fp_len; /* length of fingerprint. - * 20 for sha-1, 16 for md5. - */ - unsigned char fingerprint[20]; /* large enough to hold SHA-1 and RIPEMD160 - hashes (20 bytes), MD5 hashes just use the - first 16 bytes */ }; /* Note, that pgp_key_t is now pointer and declared in crypt.h */ typedef struct pgp_uid { char *addr; short trust; int flags; diff --git a/pgppubring.c b/pgppubring.c --- a/pgppubring.c +++ b/pgppubring.c @@ -151,16 +151,33 @@ snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr"); } pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind); return 0; } +static char *binary_fingerprint_to_string (unsigned char *buff, size_t length) +{ + int i; + char *fingerprint, *pf; + + pf = fingerprint = (char *)safe_malloc ((length * 2) + 1); + + for (i = 0; i < length; i++) + { + sprintf (pf, "%02X", buff[i]); + pf += 2; + } + *pf = 0; + + return fingerprint; +} + /* The actual key ring parser */ static void pgp_make_pgp2_fingerprint (unsigned char *buff, unsigned char *digest) { struct md5_ctx ctx; unsigned int size = 0; @@ -216,22 +233,19 @@ p->numalg = alg; p->algorithm = pgp_pkalgbytype (alg); p->flags |= pgp_get_abilities (alg); if (dump_fingerprints) { /* j now points to the key material, which we need for the fingerprint */ - p->fp_len = MD5_DIGEST_LENGTH; pgp_make_pgp2_fingerprint (&buff[j], digest); - memcpy (p->fingerprint, digest, MD5_DIGEST_LENGTH); + p->fingerprint = binary_fingerprint_to_string (digest, MD5_DIGEST_LENGTH); } - else /* just to be usre */ - memset (p->fingerprint, 0, MD5_DIGEST_LENGTH); expl = 0; for (i = 0; i < 2; i++) expl = (expl << 8) + buff[j++]; p->keylen = expl; expl = (expl + 7) / 8; @@ -337,17 +351,20 @@ if (alg >= 1 && alg <= 3) skip_bignum (buff, l, j, &j, 2); else if (alg == 17 || alg == 16 || alg == 20) skip_bignum (buff, l, j, &j, 1); pgp_make_pgp3_fingerprint (buff, j, digest); - p->fp_len = SHA_DIGEST_LENGTH; + if (dump_fingerprints) + { + p->fingerprint = binary_fingerprint_to_string (digest, SHA_DIGEST_LENGTH); + } for (k = 0; k < 2; k++) { for (id = 0, i = SHA_DIGEST_LENGTH - 8 + k * 4; i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++) id = (id << 8) + digest[i]; snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX", id); @@ -824,23 +841,20 @@ putchar (*id); else printf ("\\x%02x", (*id) & 0xff); } } static void print_fingerprint (pgp_key_t p) { - int i = 0; + if (!p->fingerprint) + return; - printf ("fpr:::::::::"); - for (i = 0; i < p->fp_len; i++) - printf ("%02X", p->fingerprint[i]); - printf (":\n"); - + printf ("fpr:::::::::%s:\n", p->fingerprint); } /* print_fingerprint() */ static void pgpring_dump_signatures (pgp_sig_t *sig) { for (; sig; sig = sig->next) { if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
# HG changeset patch # User Kevin McCarthy <ke...@8t8.us> # Date 1423882239 28800 # Fri Feb 13 18:50:39 2015 -0800 # Node ID 6213c21607b79b1faab74ce39573c17a08011fb8 # Parent 92c80d8bb14487e155a51b5e18646a655c506c28 Add fingerprint record parsing for pgp list keys. (see #3695) Modify parse_pub_line to parse fpr records and add the fingerprint to the pgp_key_t's fingerprint field. Add "--with-fingerprint --with-fingerprint" to the pgp_list_pubring_command and pgp_list_secring_command commands in contrib/gpg.rc. The second invocation generates fpr records for subkeys too. diff --git a/contrib/gpg.rc b/contrib/gpg.rc --- a/contrib/gpg.rc +++ b/contrib/gpg.rc @@ -60,20 +60,20 @@ # export a key from the public key ring set pgp_export_command="gpg --no-verbose --export --armor %r" # verify a key set pgp_verify_key_command="gpg --verbose --batch --fingerprint --check-sigs %r" # read in the public key ring -set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --list-keys %r" +set pgp_list_pubring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-keys %r" # read in the secret key ring -set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --list-secret-keys %r" +set pgp_list_secring_command="gpg --no-verbose --batch --quiet --with-colons --with-fingerprint --with-fingerprint --list-secret-keys %r" # fetch keys # set pgp_getkeys_command="pkspxycwrap %r" # pattern for good signature - may need to be adapted to locale! # set pgp_good_sign="^gpgv?: Good signature from " diff --git a/gnupgparse.c b/gnupgparse.c --- a/gnupgparse.c +++ b/gnupgparse.c @@ -116,16 +116,17 @@ } } static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k) { pgp_uid_t *uid = NULL; int field = 0, is_uid = 0; int is_pub = 0; + int is_fpr = 0; char *pend, *p; int trust = 0; int flags = 0; struct pgp_keyinfo tmp; *is_subkey = 0; if (!*buf) return NULL; @@ -143,36 +144,41 @@ for (p = buf; p; p = pend) { if ((pend = strchr (p, ':'))) *pend++ = 0; field++; if (!*p && (field != 1) && (field != 10)) continue; + if (is_fpr && (field != 10)) + continue; + switch (field) { case 1: /* record type */ { dprint (2, (debugfile, "record type: %s\n", p)); if (!mutt_strcmp (p, "pub")) is_pub = 1; else if (!mutt_strcmp (p, "sub")) *is_subkey = 1; else if (!mutt_strcmp (p, "sec")) ; else if (!mutt_strcmp (p, "ssb")) *is_subkey = 1; else if (!mutt_strcmp (p, "uid")) is_uid = 1; + else if (!mutt_strcmp (p, "fpr")) + is_fpr = 1; else return NULL; - if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB)))) + if (!(is_uid || is_fpr || (*is_subkey && option (OPTPGPIGNORESUB)))) memset (&tmp, 0, sizeof (tmp)); break; } case 2: /* trust info */ { dprint (2, (debugfile, "trust info: %s\n", p)); @@ -285,16 +291,24 @@ * We allow an empty field for a pub record type because it is * possible for a primary uid record to have an empty User-ID * field. Without any address records, it is not possible to * use the key in mutt. */ if (!(pend && (*p || is_pub))) break; + if (is_fpr) + { + /* don't let a subkey fpr overwrite and existing primary key fpr */ + if (!tmp.fingerprint) + tmp.fingerprint = safe_strdup (p); + break; + } + /* ignore user IDs on subkeys */ if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB))) break; dprint (2, (debugfile, "user ID: %s\n", NONULL (p))); uid = safe_calloc (sizeof (pgp_uid_t), 1); fix_uid (p); @@ -344,17 +358,17 @@ break; default: break; } } /* merge temp key back into real key */ - if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB)))) + if (!(is_uid || is_fpr || (*is_subkey && option (OPTPGPIGNORESUB)))) k = safe_malloc (sizeof (*k)); memcpy (k, &tmp, sizeof (*k)); /* fixup parentship of uids after mering the temp key into * the real key */ if (tmp.address) { for (uid = k->address; uid; uid = uid->next) uid->parent = k;
# HG changeset patch # User Eike Rathke <er...@erack.de> # Date 1423687117 -3600 # Wed Feb 11 21:38:37 2015 +0100 # Node ID 15394c7694ca6780261e4bae5a2ce61429ba8d55 # Parent 6213c21607b79b1faab74ce39573c17a08011fb8 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,86 @@ 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; + + /* 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 */ + s1 = s2 = pf; + do + { + c = *(s2++); + if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f')) + { + ++s1; /* hex digit */ + if (isid == 2) + isid = 1; /* it is an ID so far */ + } + else if (c) + { + isid = 0; /* not an ID */ + if (c == ' ' && (((s1 - pf) % 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 && (((s1 - pf) == 40) || ((s1 - pf) == 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,26 @@ /* 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. + 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