Skip to contentSkip to conversation

X509 (gpgsm) commit signing support

PATCH Superseded Henning Schild 4 participants 19 messages Jul 13, 2018 – Jul 17, 2018 Documentation

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 cover X509 (gpgsm) commit signing support

Changes in v3:
- drop patches 1 and 2 known from < v3, see pu hs/push-cert-check-cleanup
- dropped some testcases from p6, added two t7004 bad key/sig
- ship a binary x509 certificate for tests, no generation anymore
- silence gpgsm in tests
- switch all tests to use test_config instead of "git config"
- p4 deal with invalid input
- p3 several refactorings, see "PATCH v2 5/9" discussion

Changes in v2:
- removed trailing commas in array initializers and add leading space
- replaced assert(0) with BUG in p5
- consolidated 2 format lookups reusing get_format_data p5
- changed from format "PGP" to "openpgp", later X509 to "x509"
- use strcasecmp instead of strcmp for format matching
- introduce gpg.<format>.program in p8, no gpg.programX509 anymore
- moved t/7510 patch inbetween the two patches changing validation code
- changed t/7510 patch to use test_config and test_must_fail

This series adds support for signing commits with gpgsm.

The first 5 patches prepare for the introduction of the actual feature in patch 6.
Finally patch 7 extends the testsuite to cover the new feature.

This series can be seen as a follow up of a series that appeared under the name "gpg-interface: Multiple signing tools" in april 2018 [1]. After that series was not merged i decided to get my patches ready. The original series aimed at being generic for any sort of signing tool, while this series just introduced the X509 variant of gpg. (gpgsm)
I collected authors and reviewers of that first series and already put them on cc.

[1] https://public-inbox.org/git/[email protected]/

Henning Schild (7):
gpg-interface: add new config to select how to sign a commit t/t7510: check the validation of the new config gpg.format
gpg-interface: introduce an abstraction for multiple gpg formats
gpg-interface: do not hardcode the key string len anymore
gpg-interface: introduce new config to select per gpg format program
gpg-interface: introduce new signature format "x509" using gpgsm gpg-interface t: extend the existing GPG tests with GPGSM

 Documentation/config.txt   |  10 +++++
 gpg-interface.c            |  95 ++++++++++++++++++++++++++++++++++++---------
 t/lib-gpg.sh               |  28 ++++++++++++-
 t/lib-gpg/gpgsm-gen-key.in |   8 ++++
 t/lib-gpg/gpgsm_cert.p12   | Bin 0 -> 2652 bytes
 t/t4202-log.sh             |  39 +++++++++++++++++++
 t/t5534-push-signed.sh     |  52 +++++++++++++++++++++++++
 t/t7004-tag.sh             |  13 +++++++
 t/t7030-verify-tag.sh      |  33 ++++++++++++++++
 t/t7510-signed-commit.sh   |   9 +++++
 10 files changed, 268 insertions(+), 19 deletions(-)
 create mode 100644 t/lib-gpg/gpgsm-gen-key.in
 create mode 100644 t/lib-gpg/gpgsm_cert.p12
Signature · 1 line
2.16.4
#1 lore raw

PATCH v3 1/7 gpg-interface: add new config to select how to sign a commit

Add "gpg.format" where the user can specify which type of signature to use for commits. At the moment only "openpgp" is supported and the value is not even used. This commit prepares for a new types of signatures.

---
 Documentation/config.txt | 4 ++++
 gpg-interface.c          | 7 +++++++
 2 files changed, 11 insertions(+)
Documentation/config.txt +4 -0
index 1cc18a828..ac373e3f4 100644
@@ -1828,6 +1828,10 @@ gpg.program::
 	signed, and the program is expected to send the result to its
 	standard output.
 
+gpg.format::
+	Specifies which key format to use when signing with `--gpg-sign`.
+	Default is "openpgp", that is also the only supported value.
+
 gui.commitMsgWidth::
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +7 -0
index 09ddfbc26..960c40086 100644
@@ -7,6 +7,7 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
+static const char *gpg_format = "openpgp";
 static const char *gpg_program = "gpg";
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
@@ -138,6 +139,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
+	if (!strcmp(var, "gpg.format")) {
+		if (value && strcasecmp(value, "openpgp"))
+			return error("malformed value for %s: %s", var, value);
+		return git_config_string(&gpg_format, var, value);
+	}
+
 	if (!strcmp(var, "gpg.program")) {
 		if (!value)
 			return config_error_nonbool(var);
Signature · 1 line
2.16.4
#2 lore raw

Re: [PATCH v3 1/7] gpg-interface: add new config to select how to sign a commit

Quoted 38 lines · Henning Schild

Add "gpg.format" where the user can specify which type of signature to use for commits. At the moment only "openpgp" is supported and the value is not even used. This commit prepares for a new types of signatures.

---
 Documentation/config.txt | 4 ++++
 gpg-interface.c          | 7 +++++++
 2 files changed, 11 insertions(+)
Documentation/config.txt +4 -0
index 1cc18a828..ac373e3f4 100644
@@ -1828,6 +1828,10 @@ gpg.program::
 	signed, and the program is expected to send the result to its
 	standard output.
 
+gpg.format::
+	Specifies which key format to use when signing with `--gpg-sign`.
+	Default is "openpgp", that is also the only supported value.
+
 gui.commitMsgWidth::
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +1 -0
index 09ddfbc26..960c40086 100644
@@ -7,6 +7,7 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
+static const char *gpg_format = "openpgp";
 static const char *gpg_program = "gpg";
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb) · line 145

Quoted 7 lines
gpg-interface.c +4 -0
@@ -138,6 +139,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
+	if (!strcmp(var, "gpg.format")) {
+		if (value && strcasecmp(value, "openpgp"))
+			return error("malformed value for %s: %s", var, value);
+		return git_config_string(&gpg_format, var, value);

I may be mistaken (sorry, I read so many topics X-<) but I think the consensus was to accept only "openpgp" so that we can ensure that

[gpg "openpgp"] program = foo

etc. can be parsed more naturally without having to manually special case the subsection name to be case insensitive. In other words, strcasecmp() should just be strcmp(). Otherwise, people would get a wrong expectation that

[gpg] format = OpenPGP
[gpg "openpgp"] program = foo

should refer to the same and single OpenPGP, but that would violate the usual configuration syntax rules.

The structure of checking the error looks quite suboptimal even when we initially support the single "openpgp" at this step. I would have expected you to be writing the above like so:

	if (!value)
		return config_error_nonbool(var);
	if (strcmp(value, "openpgp"))
		return error("unsupported value for %s: %s", var, value);
	return git_config_string(...);

That would make it more clear that the variable is "nonbool", which does not change across additional support for new formats in later steps.

Quoted 5 lines
gpg-interface.c +2 -0
+	}
+
 	if (!strcmp(var, "gpg.program")) {
 		if (!value)
 			return config_error_nonbool(var);
#3 lore raw

Re: [PATCH v3 1/7] gpg-interface: add new config to select how to sign a commit

Quoted 20 lines · Junio C Hamano

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb) · line 145

Quoted 9 lines

#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"

gpg-interface.c +4 -0
@@ -138,6 +139,12 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
+	if (!strcmp(var, "gpg.format")) {
+		if (value && strcasecmp(value, "openpgp"))
+			return error("malformed value for %s: %s", var, value);
+		return git_config_string(&gpg_format, var, value);

I may be mistaken (sorry, I read so many topics X-<) but I think the consensus was to accept only "openpgp" so that we can ensure that

[gpg "openpgp"] program = foo

etc. can be parsed more naturally without having to manually special case the subsection name to be case insensitive. In other words, strcasecmp() should just be strcmp(). Otherwise, people would get a wrong expectation that

Quoted 5 lines

[gpg] format = OpenPGP
[gpg "openpgp"] program = foo

should refer to the same and single OpenPGP, but that would violate the usual configuration syntax rules.

I was the one who argued the other way. But unless we are going to move to a two-level config for all of it (i.e., gpg.openPGPProgram), I think what you suggest here is the sanest way forward.

Quoted 10 lines

The structure of checking the error looks quite suboptimal even when we initially support the single "openpgp" at this step. I would have expected you to be writing the above like so:

	if (!value)
		return config_error_nonbool(var);
	if (strcmp(value, "openpgp"))
		return error("unsupported value for %s: %s", var, value);
	return git_config_string(...);
Quoted 3 lines

That would make it more clear that the variable is "nonbool", which does not change across additional support for new formats in later steps.

Agreed.

-Peff

#4 lore raw

Re: [PATCH v3 1/7] gpg-interface: add new config to select how to sign a commit

In reply to Junio C Hamano

Am Mon, 16 Jul 2018 13:14:34 -0700
schrieb Junio C Hamano <[email protected]>:

Quoted 62 lines
Quoted 40 lines · Henning Schild

Add "gpg.format" where the user can specify which type of signature to use for commits. At the moment only "openpgp" is supported and the value is not even used. This commit prepares for a new types of signatures.

---
 Documentation/config.txt | 4 ++++
 gpg-interface.c          | 7 +++++++
 2 files changed, 11 insertions(+)
Documentation/config.txt +0 -0
index 1cc18a828..ac373e3f4 100644
@@ -1828,6 +1828,10 @@ gpg.program::
 	signed, and the program is expected to send the result to

its standard output.

Documentation/config.txt +2 -0
+gpg.format::
+	Specifies which key format to use when signing with

--gpg-sign.

Documentation/config.txt +1 -0
+	Default is "openpgp", that is also the only supported

value. +
gui.commitMsgWidth::

Documentation/config.txt +0 -0
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +1 -0
index 09ddfbc26..960c40086 100644
@@ -7,6 +7,7 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
+static const char *gpg_format = "openpgp";
 static const char *gpg_program = "gpg";
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"

gpg-interface.c int git_gpg_config(const char *var, const char · line 147

Quoted 9 lines
gpg-interface.c +4 -0
@@ -138,6 +139,12 @@ int git_gpg_config(const char *var, const char
 *value, void *cb) return 0;
 	}
 
+	if (!strcmp(var, "gpg.format")) {
+		if (value && strcasecmp(value, "openpgp"))
+			return error("malformed value for %s: %s",
 var, value);
+		return git_config_string(&gpg_format, var,
 value);  

I may be mistaken (sorry, I read so many topics X-<) but I think the consensus was to accept only "openpgp" so that we can ensure that

[gpg "openpgp"] program = foo

etc. can be parsed more naturally without having to manually special case the subsection name to be case insensitive. In other words, strcasecmp() should just be strcmp(). Otherwise, people would get a wrong expectation that

Quoted 5 lines

[gpg] format = OpenPGP
[gpg "openpgp"] program = foo

should refer to the same and single OpenPGP, but that would violate the usual configuration syntax rules.

Ok, also having read the other mails i think we are still at gpg.format and gpg.<format>.program, so i switched the two patches from strcasecmp back to strcmp.

Quoted 8 lines

The structure of checking the error looks quite suboptimal even when we initially support the single "openpgp" at this step. I would have expected you to be writing the above like so:

	if (!value)
		return config_error_nonbool(var);
	if (strcmp(value, "openpgp"))
		return error("unsupported value for %s: %s", var,
Quoted 5 lines

value); return git_config_string(...);

That would make it more clear that the variable is "nonbool", which does not change across additional support for new formats in later steps.

Right, at one point (not mailed) i had that but expected it would not pass the review, since git_config_string contains that check as well. Changed.

Henning

Quoted 5 lines
Quoted 5 lines
gpg-interface.c +2 -0
+	}
+
 	if (!strcmp(var, "gpg.program")) {
gpg-interface.c +0 -0
 		if (!value)
 			return config_error_nonbool(var);  
#5 lore raw

Re: [PATCH v3 1/7] gpg-interface: add new config to select how to sign a commit

In reply to Henning Schild
Quoted 15 lines · Henning Schild

Add "gpg.format" where the user can specify which type of signature to use for commits. At the moment only "openpgp" is supported and the value is not even used. This commit prepares for a new types of signatures.

---
 Documentation/config.txt | 4 ++++
 gpg-interface.c          | 7 +++++++
 2 files changed, 11 insertions(+)
Documentation/config.txt +0 -0
index 1cc18a828..ac373e3f4 100644

Documentation/config.txt gpg.program:: · line 1833

Quoted 6 lines
Documentation/config.txt +3 -0
@@ -1828,6 +1828,10 @@ gpg.program::
 	signed, and the program is expected to send the result to its
 	standard output.
 
+gpg.format::
+	Specifies which key format to use when signing with `--gpg-sign`.
+	Default is "openpgp", that is also the only supported value.

I think, as discussed in the other thread, perhaps a different prefix for these options is in order if we'd like to plan for the future. Maybe this could be "signature.format", "sign.format", "signing.format", or "signingtool.format" (I tend to prefer the former, but not too strongly).

I anticipate that some projects will prefer other formats, and it makes it easier if we don't have to maintain two sets of legacy names.

Signature · 2 lines
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204
#6 lore raw

Re: [PATCH v3 1/7] gpg-interface: add new config to select how to sign a commit

Quoted 10 lines · brian m. carlson

Documentation/config.txt gpg.program:: · line 1833

Quoted 3 lines
Documentation/config.txt +3 -0
+gpg.format::
+	Specifies which key format to use when signing with `--gpg-sign`.
+	Default is "openpgp", that is also the only supported value.

I think, as discussed in the other thread, perhaps a different prefix for these options is in order if we'd like to plan for the future. Maybe this could be "signature.format", "sign.format", "signing.format", or "signingtool.format" (I tend to prefer the former, but not too strongly).

Quoted 2 lines

I anticipate that some projects will prefer other formats, and it makes it easier if we don't have to maintain two sets of legacy names.

Heh. This is slowly morphing into the original signingtool series.

For the record (since I think my response is what you meant by the "other thread"), I'm OK with going down this gpg.* road for now, and dealing with other tools if and when they appear (via the extra level of indirection).

-Peff

#7 lore raw

PATCH v3 2/7 t/t7510: check the validation of the new config gpg.format

In reply to Henning Schild

Test setting gpg.format to both invalid and valid values.

---
 t/t7510-signed-commit.sh | 9 +++++++++
 1 file changed, 9 insertions(+)
t/t7510-signed-commit.sh +9 -0
index 6e2015ed9..1b6a1dd90 100755
@@ -227,4 +227,13 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' '
 	grep "gpg: Good signature" actual
 '
 
+test_expect_success GPG 'check config gpg.format values' '
+	test_config gpg.format openpgp &&
+	git commit -S --amend -m "success" &&
+	test_config gpg.format OpEnPgP &&
+	git commit -S --amend -m "success" &&
+	test_config gpg.format malformed &&
+	test_must_fail git commit -S --amend -m "fail"
+'
+
 test_done
Signature · 1 line
2.16.4
#8 lore raw

Re: [PATCH v3 2/7] t/t7510: check the validation of the new config gpg.format

Quoted 15 lines · Henning Schild

Test setting gpg.format to both invalid and valid values.

---
 t/t7510-signed-commit.sh | 9 +++++++++
 1 file changed, 9 insertions(+)
t/t7510-signed-commit.sh +0 -0
index 6e2015ed9..1b6a1dd90 100755
@@ -227,4 +227,13 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' '
 	grep "gpg: Good signature" actual
 '
 

t/t7510-signed-commit.sh test_expect_success GPG 'log.showsignature behaves like --show-signature' ' · line 239

Quoted 10 lines
t/t7510-signed-commit.sh +9 -0
+test_expect_success GPG 'check config gpg.format values' '
+	test_config gpg.format openpgp &&
+	git commit -S --amend -m "success" &&
+	test_config gpg.format OpEnPgP &&
+	git commit -S --amend -m "success" &&
+	test_config gpg.format malformed &&
+	test_must_fail git commit -S --amend -m "fail"
+'
+
 test_done

Since we'd be doing case-sensitive value for gpg.format, this would have to change in a later iteration, I guess.

#9 lore raw

PATCH v3 3/7 gpg-interface: introduce an abstraction for multiple gpg formats

In reply to Henning Schild

Create a struct that holds the format details for the supported formats. At the moment that is still just "openpgp". This commit prepares for the introduction of more formats, that might use other programs and match other signatures.

---
 gpg-interface.c | 84 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 63 insertions(+), 21 deletions(-)
gpg-interface.c +63 -21
index 960c40086..699651fd9 100644
@@ -7,11 +7,46 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
-static const char *gpg_format = "openpgp";
-static const char *gpg_program = "gpg";
+struct gpg_format {
+	const char *name;
+	const char *program;
+	const char **extra_args_verify;
+	const char **sigs;
+};
+
+static const char *openpgp_verify_args[] = { "--keyid-format=long", NULL };
+static const char *openpgp_sigs[] = {
+	"-----BEGIN PGP SIGNATURE-----",
+	"-----BEGIN PGP MESSAGE-----", NULL };
+
+static struct gpg_format gpg_formats[] = {
+	{ .name = "openpgp", .program = "gpg",
+	  .extra_args_verify = openpgp_verify_args,
+	  .sigs = openpgp_sigs
+	},
+};
+static struct gpg_format *current_format = &gpg_formats[0];
+
+static struct gpg_format *get_format_by_name(const char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		if (!strcasecmp(gpg_formats[i].name, str))
+			return gpg_formats + i;
+	return NULL;
+}
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+static struct gpg_format *get_format_by_sig(const char *sig)
+{
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		for (j = 0; gpg_formats[i].sigs[j]; j++)
+			if (starts_with(sig, gpg_formats[i].sigs[j]))
+				return gpg_formats + i;
+	return NULL;
+}
 
 void signature_check_clear(struct signature_check *sigc)
 {
@@ -102,12 +137,6 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
 		fputs(output, stderr);
 }
 
-static int is_gpg_start(const char *line)
-{
-	return starts_with(line, PGP_SIGNATURE) ||
-		starts_with(line, PGP_MESSAGE);
-}
-
 size_t parse_signature(const char *buf, size_t size)
 {
 	size_t len = 0;
@@ -115,7 +144,7 @@ size_t parse_signature(const char *buf, size_t size)
 	while (len < size) {
 		const char *eol;
 
-		if (is_gpg_start(buf + len))
+		if (get_format_by_sig(buf + len))
 			match = len;
 
 		eol = memchr(buf + len, '\n', size - len);
@@ -132,6 +161,9 @@ void set_signing_key(const char *key)
 
 int git_gpg_config(const char *var, const char *value, void *cb)
 {
+	struct gpg_format *fmt = NULL;
+	char *fmtname = NULL;
+
 	if (!strcmp(var, "user.signingkey")) {
 		if (!value)
 			return config_error_nonbool(var);
@@ -140,18 +172,23 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 	}
 
 	if (!strcmp(var, "gpg.format")) {
-		if (value && strcasecmp(value, "openpgp"))
-			return error("malformed value for %s: %s", var, value);
-		return git_config_string(&gpg_format, var, value);
-	}
-
-	if (!strcmp(var, "gpg.program")) {
 		if (!value)
 			return config_error_nonbool(var);
-		gpg_program = xstrdup(value);
+		fmt = get_format_by_name(value);
+		if (!fmt)
+			return error("malformed value for %s: %s", var, value);
+		current_format = fmt;
 		return 0;
 	}
 
+	if (!strcmp(var, "gpg.program"))
+		fmtname = "openpgp";
+
+	if (fmtname) {
+		fmt = get_format_by_name(fmtname);
+		return git_config_string(&fmt->program, var, value);
+	}
+
 	return 0;
 }
 
@@ -170,7 +207,7 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
 	struct strbuf gpg_status = STRBUF_INIT;
 
 	argv_array_pushl(&gpg.args,
-			 gpg_program,
+			 current_format->program,
 			 "--status-fd=2",
 			 "-bsau", signing_key,
 			 NULL);
@@ -208,6 +245,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
 			 struct strbuf *gpg_output, struct strbuf *gpg_status)
 {
 	struct child_process gpg = CHILD_PROCESS_INIT;
+	struct gpg_format *fmt;
 	struct tempfile *temp;
 	int ret;
 	struct strbuf buf = STRBUF_INIT;
@@ -223,10 +261,14 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
 		return -1;
 	}
 
+	fmt = get_format_by_sig(signature);
+	if (!fmt)
+		BUG("bad signature '%s'", signature);
+
+	argv_array_push(&gpg.args, fmt->program);
+	argv_array_pushv(&gpg.args, fmt->extra_args_verify);
 	argv_array_pushl(&gpg.args,
-			 gpg_program,
 			 "--status-fd=1",
-			 "--keyid-format=long",
 			 "--verify", temp->filename.buf, "-",
 			 NULL);
 
Signature · 1 line
2.16.4
#10 lore raw

Re: [PATCH v3 3/7] gpg-interface: introduce an abstraction for multiple gpg formats

Quoted 18 lines · Henning Schild

Create a struct that holds the format details for the supported formats. At the moment that is still just "openpgp". This commit prepares for the introduction of more formats, that might use other programs and match other signatures.

---
 gpg-interface.c | 84 ++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 63 insertions(+), 21 deletions(-)
gpg-interface.c +0 -0
index 960c40086..699651fd9 100644
@@ -7,11 +7,46 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;

gpg-interface.c line 17

Quoted 10 lines
gpg-interface.c +8 -2
-static const char *gpg_format = "openpgp";
-static const char *gpg_program = "gpg";
+struct gpg_format {
+	const char *name;
+	const char *program;
+	const char **extra_args_verify;
+	const char **sigs;
+};
+
+static const char *openpgp_verify_args[] = { "--keyid-format=long", NULL };

Let's write it like this, even if the current line is short enough:

static const char *openpgp_verify_args[] = {
	"--keyid-format=long",
	NULL
};

Ditto for the next one. Even if you do not expect these two arrays to get longer, people tend to copy & paste to imitate existing code when making new things, and we definitely would not want them to be doing so on a code like the above (or below). When they need to add new elements to their arrays, having the terminating NULL on its own line means they will have to see less patch noise.

gpg-interface.c line 24

Quoted 7 lines
gpg-interface.c +7 -0
+static const char *openpgp_sigs[] = {
+	"-----BEGIN PGP SIGNATURE-----",
+	"-----BEGIN PGP MESSAGE-----", NULL };
+
+static struct gpg_format gpg_formats[] = {
+	{ .name = "openpgp", .program = "gpg",
+	  .extra_args_verify = openpgp_verify_args,

If the underlying aray is "verify_args" (not "extra"), perhaps the field name should also be .verify_args to match.

Giving an array of things a name "things[]" is a disease, unless the array is very often passed around as a whole to represent the collection of everything. When the array is mostly accessed one element at a time, being able to say thing[3] to mean the third thing is much more pleasant.

So, from that point of view, I pretty much agree with openpgp_verify_args[] and openpgp_sigs[], but am against gpg_formats[]. The last one should be gpg_format[], for which we happen to have only one variant right now.

gpg-interface.c line 28

Quoted 4 lines
gpg-interface.c +4 -0
+	  .sigs = openpgp_sigs
+	},
+};
+static struct gpg_format *current_format = &gpg_formats[0];

Have a blank line before the above one.

What does "current_format" mean? Is it different from "format to be used"? As we will overwrite the variable upon reading the config, we cannot afford to call it default_format, but somehow "current" feels misleading, at least to me. I expected to find "old format" elsewhere and there may be some format conversion or something from the variable name.

    static struct gpg_format *use_format = &gpg_format[0];

perhaps?

gpg-interface.c line 35

Quoted 7 lines
gpg-interface.c +7 -0
+
+static struct gpg_format *get_format_by_name(const char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		if (!strcasecmp(gpg_formats[i].name, str))

As [1/7], this would become strcmp(), I presume?

Quoted 6 lines
gpg-interface.c +3 -2
+			return gpg_formats + i;
+	return NULL;
+}
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"

gpg-interface.c line 49

Quoted 10 lines
gpg-interface.c +10 -0
+static struct gpg_format *get_format_by_sig(const char *sig)
+{
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		for (j = 0; gpg_formats[i].sigs[j]; j++)
+			if (starts_with(sig, gpg_formats[i].sigs[j]))
+				return gpg_formats + i;
+	return NULL;
+}

OK.

Quoted 6 lines
gpg-interface.c +0 -2
@@ -140,18 +172,23 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 	}
 
 	if (!strcmp(var, "gpg.format")) {
-		if (value && strcasecmp(value, "openpgp"))
-			return error("malformed value for %s: %s", var, value);

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb) · line 179

Quoted 10 lines
gpg-interface.c +3 -5
-		return git_config_string(&gpg_format, var, value);
-	}
-
-	if (!strcmp(var, "gpg.program")) {
 		if (!value)
 			return config_error_nonbool(var);
-		gpg_program = xstrdup(value);
+		fmt = get_format_by_name(value);
+		if (!fmt)
+			return error("malformed value for %s: %s", var, value);

If I say "opongpg", that probably is not "malformed" (it is all lowercase ascii alphabet that is very plausible-looking string), but simply "bad value".

But other than the minor nit, yes, this structure is what I expected to see from the very first step 1/7.

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb)

Quoted 7 lines
gpg-interface.c +3 -0
+		current_format = fmt;
 		return 0;
 	}
 
 
+	if (!strcmp(var, "gpg.program"))
+		fmtname = "openpgp";

OK, this is a backward compatibility measure to treat gpg.program as if it were gpg.openpgp.program, which makes sense.

Quoted 7 lines
gpg-interface.c +5 -0
+	if (fmtname) {
+		fmt = get_format_by_name(fmtname);
+		return git_config_string(&fmt->program, var, value);
+	}
+
 	return 0;
 }
#11 lore raw

Re: [PATCH v3 3/7] gpg-interface: introduce an abstraction for multiple gpg formats

Am Mon, 16 Jul 2018 13:40:32 -0700
schrieb Junio C Hamano <[email protected]>:

Quoted 40 lines
Quoted 20 lines · Henning Schild

Create a struct that holds the format details for the supported formats. At the moment that is still just "openpgp". This commit prepares for the introduction of more formats, that might use other programs and match other signatures.

---
 gpg-interface.c | 84

++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 21 deletions(-)

gpg-interface.c +0 -1
index 960c40086..699651fd9 100644
@@ -7,11 +7,46 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
-static const char *gpg_format = "openpgp";

gpg-interface.c line 18

Quoted 10 lines
gpg-interface.c +8 -1
-static const char *gpg_program = "gpg";
+struct gpg_format {
+	const char *name;
+	const char *program;
+	const char **extra_args_verify;
+	const char **sigs;
+};
+
+static const char *openpgp_verify_args[] =
 { "--keyid-format=long", NULL };  

Let's write it like this, even if the current line is short enough:

static const char *openpgp_verify_args[] = {
	"--keyid-format=long",
	NULL
};
Quoted 6 lines

Ditto for the next one. Even if you do not expect these two arrays to get longer, people tend to copy & paste to imitate existing code when making new things, and we definitely would not want them to be doing so on a code like the above (or below). When they need to add new elements to their arrays, having the terminating NULL on its own line means they will have to see less patch noise.

Ok, for consistency a later patch will introduce { NULL }; on three lines.

Quoted 10 lines

gpg-interface.c line 24

Quoted 7 lines
gpg-interface.c +7 -0
+static const char *openpgp_sigs[] = {
+	"-----BEGIN PGP SIGNATURE-----",
+	"-----BEGIN PGP MESSAGE-----", NULL };
+
+static struct gpg_format gpg_formats[] = {
+	{ .name = "openpgp", .program = "gpg",
+	  .extra_args_verify = openpgp_verify_args,  

If the underlying aray is "verify_args" (not "extra"), perhaps the field name should also be .verify_args to match.

Renamed extra_args_verify to verify_args.

Quoted 10 lines

Giving an array of things a name "things[]" is a disease, unless the array is very often passed around as a whole to represent the collection of everything. When the array is mostly accessed one element at a time, being able to say thing[3] to mean the third thing is much more pleasant.

So, from that point of view, I pretty much agree with openpgp_verify_args[] and openpgp_sigs[], but am against gpg_formats[]. The last one should be gpg_format[], for which we happen to have only one variant right now.

renamed gpg_formats[] to gpg_format[].

Quoted 14 lines

gpg-interface.c line 28

Quoted 4 lines
gpg-interface.c +4 -0
+	  .sigs = openpgp_sigs
+	},
+};
+static struct gpg_format *current_format = &gpg_formats[0];  

Have a blank line before the above one.

What does "current_format" mean? Is it different from "format to be used"? As we will overwrite the variable upon reading the config, we cannot afford to call it default_format, but somehow "current" feels misleading, at least to me. I expected to find "old format" elsewhere and there may be some format conversion or something from the variable name.

Quoted 3 lines
    static struct gpg_format *use_format = &gpg_format[0];

perhaps?

renamed current_format to use_format.

Quoted 44 lines

gpg-interface.c line 35

Quoted 7 lines
gpg-interface.c +7 -0
+
+static struct gpg_format *get_format_by_name(const char *str)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		if (!strcasecmp(gpg_formats[i].name, str))  

As [1/7], this would become strcmp(), I presume?

Quoted 7 lines
gpg-interface.c +4 -2
+			return gpg_formats + i;
+	return NULL;
+}
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+static struct gpg_format *get_format_by_sig(const char *sig)

gpg-interface.c line 49

Quoted 10 lines
gpg-interface.c +9 -0
+{
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(gpg_formats); i++)
+		for (j = 0; gpg_formats[i].sigs[j]; j++)
+			if (starts_with(sig,
 gpg_formats[i].sigs[j]))
+				return gpg_formats + i;
+	return NULL;
+}  

OK.

Quoted 14 lines
gpg-interface.c +0 -7
@@ -140,18 +172,23 @@ int git_gpg_config(const char *var, const
 char *value, void *cb) }
 
 	if (!strcmp(var, "gpg.format")) {
-		if (value && strcasecmp(value, "openpgp"))
-			return error("malformed value for %s: %s",
 var, value);
-		return git_config_string(&gpg_format, var, value);
-	}
-
-	if (!strcmp(var, "gpg.program")) {
 		if (!value)
 			return config_error_nonbool(var);
-		gpg_program = xstrdup(value);
Quoted 8 lines

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb)

Quoted 4 lines
gpg-interface.c +3 -0
+		fmt = get_format_by_name(value);
+		if (!fmt)
+			return error("malformed value for %s: %s",
 var, value);  

If I say "opongpg", that probably is not "malformed" (it is all lowercase ascii alphabet that is very plausible-looking string), but simply "bad value".

Switched to "unsupported value", as suggested in another reply.

Henning

Quoted 22 lines

But other than the minor nit, yes, this structure is what I expected to see from the very first step 1/7.

gpg-interface.c int git_gpg_config(const char *var, const char *value, void *cb)

Quoted 7 lines
gpg-interface.c +3 -0
+		current_format = fmt;
 		return 0;
 	}  
 
 
+	if (!strcmp(var, "gpg.program"))
+		fmtname = "openpgp";  

OK, this is a backward compatibility measure to treat gpg.program as if it were gpg.openpgp.program, which makes sense.

Quoted 8 lines
gpg-interface.c +3 -0
+	if (fmtname) {
+		fmt = get_format_by_name(fmtname);
+		return git_config_string(&fmt->program, var,

value);

gpg-interface.c +2 -0
+	}
+
 	return 0;
 }  
#12 lore raw

PATCH v3 4/7 gpg-interface: do not hardcode the key string len anymore

In reply to Henning Schild

gnupg does print the keyid followed by a space and the signer comes next. The same pattern is also used in gpgsm, but there the key length would be 40 instead of 16. Instead of hardcoding the expected length, find the first space and calculate it.
Input that does not match the expected format will be ignored now, before we jumped to found+17 which might have been behind the end of an unexpected string.

---
 gpg-interface.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
gpg-interface.c +4 -3
index 699651fd9..93bd0fb32 100644
@@ -89,10 +89,11 @@ static void parse_gpg_output(struct signature_check *sigc)
 		sigc->result = sigcheck_gpg_status[i].result;
 		/* The trust messages are not followed by key/signer information */
 		if (sigc->result != 'U') {
-			sigc->key = xmemdupz(found, 16);
+			next = strchrnul(found, ' ');
+			sigc->key = xmemdupz(found, next - found);
 			/* The ERRSIG message is not followed by signer information */
-			if (sigc-> result != 'E') {
-				found += 17;
+			if (*next && sigc-> result != 'E') {
+				found = next + 1;
 				next = strchrnul(found, '\n');
 				sigc->signer = xmemdupz(found, next - found);
 			}
Signature · 1 line
2.16.4
#13 lore raw

Re: [PATCH v3 4/7] gpg-interface: do not hardcode the key string len anymore

Quoted 8 lines · Henning Schild

gnupg does print the keyid followed by a space and the signer comes next. The same pattern is also used in gpgsm, but there the key length would be 40 instead of 16. Instead of hardcoding the expected length, find the first space and calculate it.
Input that does not match the expected format will be ignored now, before we jumped to found+17 which might have been behind the end of an unexpected string.

Quoted 4 lines
---
 gpg-interface.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

Very nice.

Quoted 19 lines
gpg-interface.c +4 -3
index 699651fd9..93bd0fb32 100644
@@ -89,10 +89,11 @@ static void parse_gpg_output(struct signature_check *sigc)
 		sigc->result = sigcheck_gpg_status[i].result;
 		/* The trust messages are not followed by key/signer information */
 		if (sigc->result != 'U') {
-			sigc->key = xmemdupz(found, 16);
+			next = strchrnul(found, ' ');
+			sigc->key = xmemdupz(found, next - found);
 			/* The ERRSIG message is not followed by signer information */
-			if (sigc-> result != 'E') {
-				found += 17;
+			if (*next && sigc-> result != 'E') {
+				found = next + 1;
 				next = strchrnul(found, '\n');
 				sigc->signer = xmemdupz(found, next - found);
 			}
#14 lore raw

PATCH v3 5/7 gpg-interface: introduce new config to select per gpg format program

In reply to Henning Schild

Supporting multiple signing formats we will have the need to configure a custom program each. Add a new config value to cater for that.

---
 Documentation/config.txt | 5 +++++
 gpg-interface.c          | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)
Documentation/config.txt +5 -0
index ac373e3f4..c0bd80954 100644
@@ -1832,6 +1832,11 @@ gpg.format::
 	Specifies which key format to use when signing with `--gpg-sign`.
 	Default is "openpgp", that is also the only supported value.
 
+gpg.<format>.program::
+	Use this to customize the program used for the signing format you
+	chose. (see gpg.program) gpg.openpgp.program is a synonym for the
+	legacy gpg.program.
+
 gui.commitMsgWidth::
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +1 -1
index 93bd0fb32..f3c22b551 100644
@@ -182,7 +182,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	if (!strcmp(var, "gpg.program"))
+	if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
 		fmtname = "openpgp";
 
 	if (fmtname) {
Signature · 1 line
2.16.4
#15 lore raw

Re: [PATCH v3 5/7] gpg-interface: introduce new config to select per gpg format program

Documentation/config.txt gpg.format:: · line 1838

Quoted 4 lines · Henning Schild
Documentation/config.txt +4 -0
+gpg.<format>.program::
+	Use this to customize the program used for the signing format you
+	chose. (see gpg.program) gpg.openpgp.program is a synonym for the
+	legacy gpg.program.

I _think_ you meant "see gpg.format", but I am not 100% sure.

When X is a synonym for Y, Y is also a synonym for X, so technically speaking this does not matter, but when we talk about backward compatibility fallback, I think we say "OLDway is retained as a legacy synonym for NEWway", i.e. the other way around.

Also, typeset in tt what end-users would type literally, like configuration variable names, i.e.

Use this to customize the rpogram used for the signing format you chose (see gpg.format). gpg.program can still be used as a legacy synonym for gpg.openpgp.program.

Quoted 16 lines

gui.commitMsgWidth::

Documentation/config.txt +0 -0
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +1 -1
index 93bd0fb32..f3c22b551 100644
@@ -182,7 +182,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	if (!strcmp(var, "gpg.program"))
+	if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
 		fmtname = "openpgp";
 
 	if (fmtname) {
#16 lore raw

Re: [PATCH v3 5/7] gpg-interface: introduce new config to select per gpg format program

Am Mon, 16 Jul 2018 13:45:40 -0700
schrieb Junio C Hamano <[email protected]>:

Quoted 10 lines

Documentation/config.txt gpg.format:: · line 1838

Quoted 6 lines · Henning Schild
(quoted diff) +2 -0
+gpg.<format>.program::
+	Use this to customize the program used for the signing

format you

(quoted diff) +1 -0
+	chose. (see gpg.program) gpg.openpgp.program is a synonym

for the

Documentation/config.txt +1 -0
+	legacy gpg.program.  

I _think_ you meant "see gpg.format", but I am not 100% sure.

No i actually meant program, the next version just refers to both config options for further reading.

Quoted 11 lines

When X is a synonym for Y, Y is also a synonym for X, so technically speaking this does not matter, but when we talk about backward compatibility fallback, I think we say "OLDway is retained as a legacy synonym for NEWway", i.e. the other way around.

Also, typeset in tt what end-users would type literally, like configuration variable names, i.e.

Use this to customize the rpogram used for the signing format you chose (see gpg.format). gpg.program can still be used as a legacy synonym for gpg.openpgp.program.

Used that second sentence.

Henning

Quoted 16 lines
Quoted 16 lines

gui.commitMsgWidth::

Documentation/config.txt +0 -0
 	Defines how wide the commit message window is in the
 	linkgit:git-gui[1]. "75" is the default.
gpg-interface.c +1 -1
index 93bd0fb32..f3c22b551 100644
@@ -182,7 +182,7 @@ int git_gpg_config(const char *var, const char
 *value, void *cb) return 0;
 	}
 
-	if (!strcmp(var, "gpg.program"))
+	if (!strcmp(var, "gpg.program") || !strcmp(var,
 "gpg.openpgp.program")) fmtname = "openpgp";
 
 	if (fmtname) {  
#17 lore raw

PATCH v3 6/7 gpg-interface: introduce new signature format "x509" using gpgsm

In reply to Henning Schild

This commit allows git to create and check x509 type signatures using gpgsm.

---
 Documentation/config.txt | 5 +++--
 gpg-interface.c          | 9 +++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)
Documentation/config.txt +3 -2
index c0bd80954..7cfce4dc8 100644
@@ -1830,12 +1830,13 @@ gpg.program::
 
 gpg.format::
 	Specifies which key format to use when signing with `--gpg-sign`.
-	Default is "openpgp", that is also the only supported value.
+	Default is "openpgp" and another possible value is "x509".
 
 gpg.<format>.program::
 	Use this to customize the program used for the signing format you
 	chose. (see gpg.program) gpg.openpgp.program is a synonym for the
-	legacy gpg.program.
+	legacy gpg.program, while the default gpg.x509.program is "gpgsm".
+
 
 gui.commitMsgWidth::
 	Defines how wide the commit message window is in the
gpg-interface.c +9 -0
index f3c22b551..62a9acf68 100644
@@ -18,12 +18,18 @@ static const char *openpgp_verify_args[] = { "--keyid-format=long", NULL };
 static const char *openpgp_sigs[] = {
 	"-----BEGIN PGP SIGNATURE-----",
 	"-----BEGIN PGP MESSAGE-----", NULL };
+static const char *x509_verify_args[] = { NULL };
+static const char *x509_sigs[] = { "-----BEGIN SIGNED MESSAGE-----", NULL };
 
 static struct gpg_format gpg_formats[] = {
 	{ .name = "openpgp", .program = "gpg",
 	  .extra_args_verify = openpgp_verify_args,
 	  .sigs = openpgp_sigs
 	},
+	{ .name = "x509", .program = "gpgsm",
+	  .extra_args_verify = x509_verify_args,
+	  .sigs = x509_sigs
+	},
 };
 static struct gpg_format *current_format = &gpg_formats[0];
 
@@ -185,6 +191,9 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 	if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
 		fmtname = "openpgp";
 
+	if (!strcmp(var, "gpg.x509.program"))
+		fmtname = "x509";
+
 	if (fmtname) {
 		fmt = get_format_by_name(fmtname);
 		return git_config_string(&fmt->program, var, value);
Signature · 1 line
2.16.4
#18 lore raw

PATCH v3 7/7 gpg-interface t: extend the existing GPG tests with GPGSM

In reply to Henning Schild

Add test cases to cover the new X509/gpgsm support. Most of them resemble existing ones. They just switch the format to x509 and set the signingkey when creating signatures. Validation of signatures does not need any configuration of git, it does need gpgsm to be configured to trust the key(-chain).
Several of the testcases build on top of existing gpg testcases. The commit ships a self-signed key for [email protected] and configures gpgsm to trust it.

---
 t/lib-gpg.sh               |  28 +++++++++++++++++++++++-
 t/lib-gpg/gpgsm-gen-key.in |   8 +++++++
 t/lib-gpg/gpgsm_cert.p12   | Bin 0 -> 2652 bytes
 t/t4202-log.sh             |  39 ++++++++++++++++++++++++++++++++++
 t/t5534-push-signed.sh     |  52 +++++++++++++++++++++++++++++++++++++++++++++
 t/t7004-tag.sh             |  13 ++++++++++++
 t/t7030-verify-tag.sh      |  33 ++++++++++++++++++++++++++++
 7 files changed, 172 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-gpg/gpgsm-gen-key.in
 create mode 100644 t/lib-gpg/gpgsm_cert.p12
t/lib-gpg.sh +27 -1
index a5d3b2cba..3fe02876c 100755
@@ -38,7 +38,33 @@ then
 			"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
 		gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \
 			--sign -u [email protected] &&
-		test_set_prereq GPG
+		test_set_prereq GPG &&
+		# Available key info:
+		# * see t/lib-gpg/gpgsm-gen-key.in
+		# To generate new certificate:
+		#  * no passphrase
+		#	gpgsm --homedir /tmp/gpghome/ \
+		#		-o /tmp/gpgsm.crt.user \
+		#		--generate-key \
+		#		--batch t/lib-gpg/gpgsm-gen-key.in
+		# To import certificate:
+		#	gpgsm --homedir /tmp/gpghome/ \
+		#		--import /tmp/gpgsm.crt.user
+		# To export into a .p12 we can later import:
+		#	gpgsm --homedir /tmp/gpghome/ \
+		#		-o t/lib-gpg/gpgsm_cert.p12 \
+		#		--export-secret-key-p12 "[email protected]"
+		echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \
+			--passphrase-fd 0 --pinentry-mode loopback \
+			--import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
+		gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K \
+			| grep fingerprint: | cut -d" " -f4 | tr -d '\n' > \
+			${GNUPGHOME}/trustlist.txt &&
+		echo " S relax" >> ${GNUPGHOME}/trustlist.txt &&
+		(gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
+		echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
+			-u [email protected] -o /dev/null --sign - 2>&1 &&
+		test_set_prereq GPGSM
 		;;
 	esac
 fi
t/lib-gpg/gpgsm-gen-key.in new file +8 -0
new file mode 100644
index 000000000..a7fd87c06
@@ -0,0 +1,8 @@
+Key-Type: RSA
+Key-Length: 2048
+Key-Usage: sign
+Serial: random
+Name-DN: CN=C O Mitter, O=Example, SN=C O, GN=Mitter
+Name-Email: [email protected]
+Not-Before: 1970-01-01 00:00:00
+Not-After: 3000-01-01 00:00:00
t/lib-gpg/gpgsm_cert.p12 new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..94ffad0d31a3b6c4e849b29d960762e485ba7ea8
GIT binary patch
t/t4202-log.sh +39 -0
index 25b1f8cc7..f57781e39 100755
@@ -1556,12 +1556,28 @@ test_expect_success GPG 'setup signed branch' '
 	git commit -S -m signed_commit
 '
 
+test_expect_success GPGSM 'setup signed branch x509' '
+	test_when_finished "git reset --hard && git checkout master" &&
+	git checkout -b signed-x509 master &&
+	echo foo >foo &&
+	git add foo &&
+	test_config gpg.format x509 &&
+	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+	git commit -S -m signed_commit
+'
+
 test_expect_success GPG 'log --graph --show-signature' '
 	git log --graph --show-signature -n1 signed >actual &&
 	grep "^| gpg: Signature made" actual &&
 	grep "^| gpg: Good signature" actual
 '
 
+test_expect_success GPGSM 'log --graph --show-signature x509' '
+	git log --graph --show-signature -n1 signed-x509 >actual &&
+	grep "^| gpgsm: Signature made" actual &&
+	grep "^| gpgsm: Good signature" actual
+'
+
 test_expect_success GPG 'log --graph --show-signature for merged tag' '
 	test_when_finished "git reset --hard && git checkout master" &&
 	git checkout -b plain master &&
@@ -1581,6 +1597,29 @@ test_expect_success GPG 'log --graph --show-signature for merged tag' '
 	grep "^| | gpg: Good signature" actual
 '
 
+test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' '
+	test_when_finished "git reset --hard && git checkout master" &&
+	test_config gpg.format x509 &&
+	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+	git checkout -b plain-x509 master &&
+	echo aaa >bar &&
+	git add bar &&
+	git commit -m bar_commit &&
+	git checkout -b tagged-x509 master &&
+	echo bbb >baz &&
+	git add baz &&
+	git commit -m baz_commit &&
+	git tag -s -m signed_tag_msg signed_tag_x509 &&
+	git checkout plain-x509 &&
+	git merge --no-ff -m msg signed_tag_x509 &&
+	git log --graph --show-signature -n1 plain-x509 >actual &&
+	grep "^|\\\  merged tag" actual &&
+	grep "^| | gpgsm: Signature made" actual &&
+	grep "^| | gpgsm: Good signature" actual &&
+	git config --unset gpg.format &&
+	git config --unset user.signingkey
+'
+
 test_expect_success GPG '--no-show-signature overrides --show-signature' '
 	git log -1 --show-signature --no-show-signature signed >actual &&
 	! grep "^gpg:" actual
t/t5534-push-signed.sh +52 -0

56 lines not on this page · raw message

t/t7004-tag.sh +13 -0
index d7b319e91..2147938aa 100755
@@ -1354,6 +1354,19 @@ test_expect_success GPG \
 	'test_config gpg.program echo &&
 	 test_must_fail git tag -s -m tail tag-gpg-failure'
 
+# try to sign with bad user.signingkey
+test_expect_success GPGSM \
+	'git tag -s fails if gpgsm is misconfigured (bad key)' \
+	'test_config user.signingkey BobTheMouse &&
+	 test_config gpg.format x509 &&
+	 test_must_fail git tag -s -m tail tag-gpg-failure'
+
+# try to produce invalid signature
+test_expect_success GPGSM \
+	'git tag -s fails if gpgsm is misconfigured (bad signature format)' \
+	'test_config gpg.x509.program echo &&
+	 test_config gpg.format x509 &&
+	 test_must_fail git tag -s -m tail tag-gpg-failure'
 
 # try to verify without gpg:
 
t/t7030-verify-tag.sh +33 -0
index 291a1e2b0..db6295d65 100755
@@ -41,6 +41,13 @@ test_expect_success GPG 'create signed tags' '
 	git tag -uB7227189 -m eighth eighth-signed-alt
 '
 
+test_expect_success GPGSM 'create signed tags x509 ' '
+	test_config gpg.format x509 &&
+	test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+	echo 9 >file && test_tick && git commit -a -m "nineth gpgsm-signed" &&
+	git tag -s -m nineth nineth-signed-x509
+'
+
 test_expect_success GPG 'verify and show signatures' '
 	(
 		for tag in initial second merge fourth-signed sixth-signed seventh-signed
@@ -72,6 +79,13 @@ test_expect_success GPG 'verify and show signatures' '
 	)
 '
 
+test_expect_success GPGSM 'verify and show signatures x509' '
+	git verify-tag nineth-signed-x509 2>actual &&
+	grep "Good signature from" actual &&
+	! grep "BAD signature from" actual &&
+	echo nineth-signed-x509 OK
+'
+
 test_expect_success GPG 'detect fudged signature' '
 	git cat-file tag seventh-signed >raw &&
 	sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
@@ -112,6 +126,12 @@ test_expect_success GPG 'verify signatures with --raw' '
 	)
 '
 
+test_expect_success GPGSM 'verify signatures with --raw x509' '
+	git verify-tag --raw nineth-signed-x509 2>actual &&
+	grep "GOODSIG" actual &&
+	! grep "BADSIG" actual &&
+	echo nineth-signed-x509 OK
+'
 test_expect_success GPG 'verify multiple tags' '
 	tags="fourth-signed sixth-signed seventh-signed" &&
 	for i in $tags
@@ -125,6 +145,19 @@ test_expect_success GPG 'verify multiple tags' '
 	test_cmp expect.stderr actual.stderr
 '
 
+test_expect_success GPGSM 'verify multiple tags x509' '
+	tags="seventh-signed nineth-signed-x509" &&
+	for i in $tags
+	do
+		git verify-tag -v --raw $i || return 1
+	done >expect.stdout 2>expect.stderr.1 &&
+	grep "^.GNUPG:." <expect.stderr.1 >expect.stderr &&
+	git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+	grep "^.GNUPG:." <actual.stderr.1 >actual.stderr &&
+	test_cmp expect.stdout actual.stdout &&
+	test_cmp expect.stderr actual.stderr
+'
+
 test_expect_success GPG 'verifying tag with --format' '
 	cat >expect <<-\EOF &&
 	tagname : fourth-signed
Signature · 1 line
2.16.4