From e36b2d1bce4bb6281e18f53d06d7831e6d6f5a09 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 4 Sep 2023 17:03:01 +0200 Subject: [PATCH] New mode to list a v5 fingerprint for v4 packets. * src/gpgme.h.in (GPGME_KEYLIST_MODE_WITH_V5FPR): New. (struct _gpgme_subkey): Add field v5fpr. * src/engine-gpg.c (gpg_keylist_build_options): Pass new option to gpg. * src/key.c (gpgme_key_unref): Free new field. * src/keylist.c (op_data_t): Parse and add "fp2" line. * tests/run-keylist.c (show_usage): Add option --v5fpr. * src/keylist.c (op_data_t): Add field failure_code. (keylist_status_handler): Handle special value. (gpgme_op_keylist_end): Return an error if a FAILURE line has been seen. -- Note that the failure code part has been added to better diagnose problems if a wrong gpg version is used. If verything works right we should not get this because we check that the gnupg version sis either >= 2.4.4 or less than 2.3 and >= 2.2.42. Note further that the v5fpr field may also be used to get the SHA-256 fingerprint of X.509 certificates (even without passing the new mode flag). GnuPG-bug-id: 6705 --- NEWS | 3 +++ doc/gpgme.texi | 10 ++++++++++ src/engine-gpg.c | 6 ++++++ src/gpgme.h.in | 4 ++++ src/key.c | 1 + src/keylist.c | 46 +++++++++++++++++++++++++++++++++++++++++++-- tests/run-keylist.c | 12 ++++++++++-- 7 files changed, 78 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 1862faf8..6fcb6182 100644 --- a/NEWS +++ b/NEWS @@ -3,10 +3,13 @@ Noteworthy changes in version 1.23.0 (unreleased) * Support GPGME_ENCRYPT_ALWAYS_TRUST also for S/MIME. [T6559] + * New keylist mode GPGME_KEYLIST_MODE_WITH_V5FPR. [T6705] + * qt: Support refreshing keys via WKD. [T6672] * Interface changes relative to the 1.22.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + GPGME_KEYLIST_MODE_WITH_V5FPR NEW. qt: Protocol::wkdRefreshJob NEW. qt: WKDRefreshJob NEW. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 714ff916..bce1b163 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2901,6 +2901,12 @@ option also makes sure that the keygrip is available in the output. The @code{GPGME_KEYLIST_MODE_EPHEMERAL} symbol specifies that keys flagged as ephemeral are included in the listing. +@item GPGME_KEYLIST_MODE_WITH_V5FPR +@since{1.23.0} + +The @code{GPGME_KEYLIST_MODE_WITH_V5FPR} symbol specifies that key +listings shall also provide v5 style fingerprints for v4 OpenPGp keys. + @item GPGME_KEYLIST_MODE_VALIDATE @since{0.4.5} @@ -3610,6 +3616,10 @@ This is the key ID of the subkey in hexadecimal digits. This is the fingerprint of the subkey in hexadecimal digits, if available. +@item char *v5fpr +For a v4 OpenPGP key this is its v5 style fingerprint of the subkey in +hexadecimal digits, if available. + @item char *keygrip @since{1.7.0} diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 99667c6d..8c9f62fb 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -3267,6 +3267,12 @@ gpg_keylist_build_options (engine_gpg_t gpg, int secret_only, err = add_arg (gpg, "--with-fingerprint"); } + if (!err && (mode & GPGME_KEYLIST_MODE_WITH_V5FPR) + && (have_gpg_version (gpg, "2.4.4") + || (have_gpg_version (gpg, "2.2.42") + && !have_gpg_version (gpg, "2.3.0")))) + err = add_arg (gpg, "--with-v5-fingerprint"); + if (!err && (mode & GPGME_KEYLIST_MODE_WITH_TOFU) && have_gpg_version (gpg, "2.1.16")) err = add_arg (gpg, "--with-tofu-info"); diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 1d3c1445..7110648e 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -384,6 +384,7 @@ gpgme_protocol_t; #define GPGME_KEYLIST_MODE_EPHEMERAL 128 #define GPGME_KEYLIST_MODE_VALIDATE 256 #define GPGME_KEYLIST_MODE_FORCE_EXTERN 512 +#define GPGME_KEYLIST_MODE_WITH_V5FPR 1024 #define GPGME_KEYLIST_MODE_LOCATE (1|2) #define GPGME_KEYLIST_MODE_LOCATE_EXTERNAL (1|2|512) @@ -616,6 +617,9 @@ struct _gpgme_subkey /* The keygrip of the subkey in hex digit form or NULL if not available. */ char *keygrip; + + /* For OpenPGP the v5 fpr of a v4 key. For X.509 the SHA256 fingerprint. */ + char *v5fpr; }; typedef struct _gpgme_subkey *gpgme_subkey_t; diff --git a/src/key.c b/src/key.c index 322bd0ac..93cdc1c8 100644 --- a/src/key.c +++ b/src/key.c @@ -342,6 +342,7 @@ gpgme_key_unref (gpgme_key_t key) { gpgme_subkey_t next = subkey->next; free (subkey->fpr); + free (subkey->v5fpr); free (subkey->curve); free (subkey->keygrip); free (subkey->card_number); diff --git a/src/keylist.c b/src/keylist.c index 3375f2ef..56836b5a 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -58,6 +58,9 @@ typedef struct /* The error code from ERROR keydb_search. */ gpgme_error_t keydb_search_err; + /* The error code from a FAILURE status line or 0. */ + gpg_error_t failure_code; + gpgme_key_t tmp_key; /* This points to the last uid in tmp_key. */ @@ -146,6 +149,13 @@ keylist_status_handler (void *priv, gpgme_status_code_t code, char *args) err = 0; break; + case GPGME_STATUS_FAILURE: + opd->failure_code = _gpgme_parse_failure (args); + if (opd->failure_code && !strcmp (args, "option-parser") + && gpg_err_code (opd->failure_code) == GPG_ERR_GENERAL) + err = gpg_error (GPG_ERR_INV_ENGINE); + break; + case GPGME_STATUS_IMPORT_OK: case GPGME_STATUS_IMPORT_PROBLEM: case GPGME_STATUS_IMPORT_RES: @@ -570,7 +580,7 @@ keylist_colon_handler (void *priv, char *line) gpgme_ctx_t ctx = (gpgme_ctx_t) priv; enum { - RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_GRP, + RT_NONE, RT_SIG, RT_UID, RT_TFS, RT_SUB, RT_PUB, RT_FPR, RT_FP2, RT_GRP, RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK } rectype = RT_NONE; @@ -623,6 +633,8 @@ keylist_colon_handler (void *priv, char *line) rectype = RT_CRS; else if (!strcmp (field[0], "fpr") && key) rectype = RT_FPR; + else if (!strcmp (field[0], "fp2") && key) + rectype = RT_FP2; else if (!strcmp (field[0], "grp") && key) rectype = RT_GRP; else if (!strcmp (field[0], "uid") && key) @@ -914,6 +926,27 @@ keylist_colon_handler (void *priv, char *line) } break; + case RT_FP2: + /* Either the SHA256 fingerprint of an X.509 cert or the + * alternate fingerprint of a v4 OpenPGP packet. (We take only + * the first one). */ + if (fields >= 10 && field[9] && *field[9]) + { + /* Need to apply it to the last subkey because all subkeys + do have fingerprints. */ + subkey = key->_last_subkey; + if (!subkey->v5fpr) + { + subkey->v5fpr = strdup (field[9]); + if (!subkey->v5fpr) + return gpg_error_from_syserror (); + } + /* Note that we don't store a copy in the key object as we + * do with the standard fingerprint. */ + } + break; + + case RT_GRP: /* Field 10 has the keygrip. */ if (fields >= 10 && field[9] && *field[9]) @@ -1299,12 +1332,21 @@ gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key) gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx) { + void *hook; + op_data_t opd; + gpg_error_t err; + TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx, ""); if (!ctx) return gpg_error (GPG_ERR_INV_VALUE); - return 0; + err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL); + opd = hook; + if (!err && opd && opd->failure_code) + err = opd->failure_code; + + return err; } diff --git a/tests/run-keylist.c b/tests/run-keylist.c index c662e902..d517cac5 100644 --- a/tests/run-keylist.c +++ b/tests/run-keylist.c @@ -55,6 +55,7 @@ show_usage (int ex) " --tofu use GPGME_KEYLIST_MODE_TOFU\n" " --sig-notations use GPGME_KEYLIST_MODE_SIG_NOTATIONS\n" " --ephemeral use GPGME_KEYLIST_MODE_EPHEMERAL\n" + " --v5fpr use GPGME_KEYLIST_MODE_V5FPR\n" " --validate use GPGME_KEYLIST_MODE_VALIDATE\n" " --import import all keys\n" " --offline use offline mode\n" @@ -179,9 +180,14 @@ main (int argc, char **argv) mode |= GPGME_KEYLIST_MODE_VALIDATE; argc--; argv++; } - else if (!strcmp (*argv, "--with-secret")) + else if (!strcmp (*argv, "--validate")) { - mode |= GPGME_KEYLIST_MODE_WITH_SECRET; + mode |= GPGME_KEYLIST_MODE_VALIDATE; + argc--; argv++; + } + else if (!strcmp (*argv, "--v5fpr")) + { + mode |= GPGME_KEYLIST_MODE_WITH_V5FPR; argc--; argv++; } else if (!strcmp (*argv, "--import")) @@ -305,6 +311,8 @@ main (int argc, char **argv) for (nsub=0; subkey; subkey = subkey->next, nsub++) { printf ("fpr %2d: %s\n", nsub, nonnull (subkey->fpr)); + if (subkey->v5fpr) + printf ("v5fpr %2d: %s\n", nsub, nonnull (subkey->v5fpr)); if (subkey->keygrip) printf ("grip %2d: %s\n", nsub, subkey->keygrip); if (subkey->curve)