git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 4/6] gpg-interface: extract hash algorithm from signature status output

From
Christian Couder <christian.couder@gmail.com>
Date
May 26, 2025, 10:33 UTC
Message-ID
<20250526103314.1542316-5-christian.couder@gmail.com>
In-Reply-To
<20250526103314.1542316-1-christian.couder@gmail.com>

When using GPG/GPGSM to verify OpenPGP/X.509 signatures, the verification result (good/bad/etc.), signer, and key fingerprint are extracted from the output, but not the specific hash algorithm (e.g., "sha1", "sha256") reported by GPG as having been used for the signature itself.

Let's improve the `gpg-interface` parsing logic to capture this information.

This information can be useful for Git commands or external tools that process signature information. For example, it could be used when displaying signature verification results to users or when working with various signature formats in tools like fast-export and fast-import.

GPG provides the hash algorithm ID used for the signature within its machine-readable status output, specifically in the fields following the `VALIDSIG` and `ERRSIG` keywords, as documented in GnuPG's `doc/DETAILS`.

The implementation follows RFC 4880 (OpenPGP Message Format) section 9.4 for the mapping between hash algorithm IDs and their corresponding names.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 gpg-interface.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
 gpg-interface.h |  4 +++
 2 files changed, 78 insertions(+)
diff --git a/gpg-interface.c b/gpg-interface.c
index e7af82d123..15687ede43 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -153,6 +153,7 @@ void signature_check_clear(struct signature_check *sigc)
 	FREE_AND_NULL(sigc->key);
 	FREE_AND_NULL(sigc->fingerprint);
 	FREE_AND_NULL(sigc->primary_key_fingerprint);
+	FREE_AND_NULL(sigc->sig_algo);
 }
 
 /* An exclusive status -- only one of them can appear in output */
@@ -221,6 +222,65 @@ static int parse_gpg_trust_level(const char *level,
 	return 1;
 }
 
+/* See RFC 4880: OpenPGP Message Format, section 9.4. Hash Algorithms */
+static struct sigcheck_gpg_hash_algo {
+	const char *id;
+	const char *name;
+} sigcheck_gpg_hash_algo[] = {
+	{ "1", "md5" },       /* deprecated */
+	{ "2", "sha1" },      /* mandatory */
+	{ "3", "ripemd160" },
+	{ "8", "sha256" },
+	{ "9", "sha384" },
+	{ "10", "sha512" },
+	{ "11", "sha224" },
+};
+
+static const char *lookup_gpg_hash_algo(const char *algo_id)
+{
+	if (!algo_id)
+		return NULL;
+
+	for (size_t i = 0; i < ARRAY_SIZE(sigcheck_gpg_hash_algo); i++) {
+		if (!strcmp(sigcheck_gpg_hash_algo[i].id, algo_id))
+			return sigcheck_gpg_hash_algo[i].name;
+	}
+
+	return NULL;
+}
+
+static char *extract_gpg_hash_algo(const char *args_start,
+				   const char *line_end,
+				   int field_index)
+{
+	const char *p = args_start;
+	int current_field = 0;
+	char *result = NULL;
+
+	while (p < line_end && current_field < field_index) {
+		/* Skip to the end of the current field */
+		while (p < line_end && *p != ' ')
+			p++;
+		/* Skip spaces to get to the start of the next field */
+		while (p < line_end && *p == ' ') {
+			p++;
+			current_field++;
+		}
+	}
+
+	if (p < line_end && current_field == field_index) {
+		/* Found start of the target field */
+		const char *algo_id_end = strchrnul(p, ' ');
+		char *algo_id = xmemdupz(p, algo_id_end - p);
+		const char *hash_algo = lookup_gpg_hash_algo(algo_id);
+		if (hash_algo)
+			result = xstrdup(hash_algo);
+		free(algo_id);
+	}
+
+	return result;
+}
+
 static void parse_gpg_output(struct signature_check *sigc)
 {
 	const char *buf = sigc->gpg_status;
@@ -242,6 +302,18 @@ static void parse_gpg_output(struct signature_check *sigc)
 		/* Iterate over all search strings */
 		for (size_t i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
 			if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
+
+				/* Do we have hash algorithm? */
+				if (!sigc->sig_algo) {
+					const char *line_end = strchrnul(line, '\n');
+					if (!strcmp(sigcheck_gpg_status[i].check, "VALIDSIG "))
+						/* Hash algorithm is the 8th field in VALIDSIG */
+						sigc->sig_algo = extract_gpg_hash_algo(line, line_end, 7);
+					else if (!strcmp(sigcheck_gpg_status[i].check, "ERRSIG "))
+						/* Hash algorithm is the 3rd field in ERRSIG */
+						sigc->sig_algo = extract_gpg_hash_algo(line, line_end, 2);
+				}
+
 				/*
 				 * GOODSIG, BADSIG etc. can occur only once for
 				 * each signature.  Therefore, if we had more
@@ -323,6 +395,7 @@ static void parse_gpg_output(struct signature_check *sigc)
 			}
 		}
 	}
+
 	return;
 
 error:
@@ -332,6 +405,7 @@ static void parse_gpg_output(struct signature_check *sigc)
 	FREE_AND_NULL(sigc->fingerprint);
 	FREE_AND_NULL(sigc->signer);
 	FREE_AND_NULL(sigc->key);
+	FREE_AND_NULL(sigc->sig_algo);
 }
 
 static int verify_gpg_signed_buffer(struct signature_check *sigc,
diff --git a/gpg-interface.h b/gpg-interface.h
index 9a32dd6ce8..2b7701ca2c 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -42,6 +42,10 @@ struct signature_check {
 	char *key;
 	char *fingerprint;
 	char *primary_key_fingerprint;
+
+	/* hash algo for GPG/GPGSM, key type for SSH */
+	char *sig_algo;
+
 	enum signature_trust_level trust_level;
 };
 
-- 
2.49.0.609.g63c55177e5
Previous: Christian CouderNext: Christian Couder
Message 25 of 65 in “fast-(import|export): improve on the signature algorithm name”
  1. fast-(import|export): improve on the signature algorithm nameChristian Couder, Apr 24, 2025
  2. Junio C HamanoApr 24, 2025
  3. Elijah NewrenApr 24, 2025
  4. Junio C HamanoApr 24, 2025
  5. Christian CouderMay 26, 2025
  6. Junio C HamanoMay 27, 2025
  7. Junio C HamanoMay 28, 2025
  8. Elijah NewrenMay 28, 2025
  9. Junio C HamanoMay 28, 2025
  10. Elijah NewrenMay 28, 2025
  11. Junio C HamanoMay 29, 2025
  12. Christian CouderJun 2, 2025
  13. Christian CouderJun 2, 2025
  14. Junio C HamanoJun 2, 2025
  15. Christian CouderMay 26, 2025
  16. Elijah NewrenApr 24, 2025
  17. Christian CouderMay 26, 2025
  18. brian m. carlsonApr 24, 2025
  19. Christian CouderMay 26, 2025
  20. Junio C HamanoApr 24, 2025
  21. 0/6 extract algo information from signaturesChristian Couder, May 26, 2025
  22. 1/6 gpg-interface: simplify ssh fingerprint parsingChristian Couder, May 26, 2025
  23. 2/6 gpg-interface: use left shift to define GPG_VERIFY_*Christian Couder, May 26, 2025
  24. 3/6 doc/verify-commit: update and improve the whole docChristian Couder, May 26, 2025
  25. 4/6 gpg-interface: extract hash algorithm from signature status outputChristian Couder, May 26, 2025
  26. 5/6 gpg-interface: extract SSH key type from signature status outputChristian Couder, May 26, 2025
  27. 6/6 verify-commit: add a --summary flagChristian Couder, May 26, 2025
  28. Elijah NewrenMay 26, 2025
  29. Christian CouderJun 19, 2025
  30. brian m. carlsonJun 2, 2025
  31. Christian CouderJun 19, 2025
  32. fast-(import|export): improve on commit signature output formatChristian Couder, Jun 18, 2025
  33. fast-(import|export): improve on commit signature output formatChristian Couder, Jun 19, 2025
  34. Junio C HamanoJun 19, 2025
  35. Christian CouderJul 8, 2025
  36. Elijah NewrenJun 19, 2025
  37. Christian CouderJun 20, 2025
  38. Junio C HamanoJun 20, 2025
  39. Christian CouderJul 8, 2025
  40. Elijah NewrenJun 26, 2025
  41. Christian CouderJul 8, 2025
  42. Junio C HamanoJul 7, 2025
  43. Christian CouderJul 8, 2025
  44. Junio C HamanoJul 8, 2025
  45. Patrick SteinhardtJul 8, 2025
  46. Christian CouderJul 8, 2025
  47. Junio C HamanoJul 8, 2025
  48. Christian CouderJul 9, 2025
  49. Junio C HamanoJul 9, 2025
  50. Patrick SteinhardtJul 10, 2025
  51. Christian CouderJul 10, 2025
  52. Junio C HamanoJul 10, 2025
  53. Christian CouderJul 8, 2025
  54. fast-(import|export): improve on commit signature output formatChristian Couder, Jul 8, 2025
  55. Junio C HamanoJul 8, 2025
  56. Elijah NewrenJul 8, 2025
  57. Junio C HamanoJul 9, 2025
  58. Elijah NewrenJul 9, 2025
  59. Christian CouderJul 9, 2025
  60. Christian CouderJul 9, 2025
  61. fast-(import|export): improve on commit signature output formatChristian Couder, Jul 9, 2025
  62. Junio C HamanoJul 9, 2025
  63. Elijah NewrenJul 14, 2025
  64. Junio C HamanoJul 14, 2025
  65. Christian CouderJul 25, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.