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

Re: [PATCH v3 1/9] Add commit, tag & push signing via SSH keys

From
Junio C Hamano <gitster@pobox.com>
Date
Jul 14, 2021, 18:19 UTC
Message-ID
<xmqqlf68wyfa.fsf@gitster.g>
In-Reply-To
<390a8f816cda0574cabe49e9f88ae1803142fb51.1626264613.git.gitgitgadget@gmail.com>
"Fabian Stelzer via GitGitGadget" <gitgitgadget@gmail.com> writes:
> From: Fabian Stelzer <fs@gigacodes.de>
> Subject: [PATCH v3 1/9] Add commit, tag & push signing via SSH keys

If you chose "ssh signing:" as the common prefix for the series, use it consistently with this step, too.

Show 6 quoted lines
> Openssh v8.2p1 added some new options to ssh-keygen for signature
> creation and verification. These allow us to use ssh keys for git
> signatures easily.
>
> Start with adding the new signature format, new config options and
> rename some fields for consistency.
OK.
> This feature makes git signing much more accessible to the average user.
> Usually they have a SSH Key for pushing code already. Using it
> for signing commits allows us to verify not only the transport but the
> pushed code as well.

Drop this paragraph or at least tone it down. It may hold true only around your immediate circle but it is far from clear and obvious. I'd expect more people push over https:// than ssh://.

We do not really require a new feature to make much more accessible for wide average user---making it just a bit more accessible to folks in your immediate circle is perfectly fine, as long as you are not harming other people ;-)

> In our corporate environemnt we use PIV x509 Certs on Yubikeys for email
> signing/encryption and ssh keys which i think is quite common
Upcase "I".
Show 8 quoted lines
> (at least for the email part). This way we can establish the correct
> trust for the SSH Keys without setting up a separate GPG Infrastructure
> (which is still quite painful for users) or implementing x509 signing
> support for git (which lacks good forwarding mechanisms).
> Using ssh agent forwarding makes this feature easily usable in todays
> development environments where code is often checked out in remote VMs / containers.
> In such a setup the keyring & revocationKeyring can be centrally
> generated from the x509 CA information and distributed to the users.

All of the above promises a wonderful new world, but what is left unclear is with this step alone how much of the new world we already gain. When you ask others to read and understand your code, please give them a bit more hint to guide them what to expect and where you are taking them next.

Show 14 quoted lines
> diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
> index 0f66818e0f8..1d7b64fa021 100644
> --- a/fmt-merge-msg.c
> +++ b/fmt-merge-msg.c
> @@ -527,10 +527,10 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
>  			len = payload.len;
>  			if (check_signature(payload.buf, payload.len, sig.buf,
>  					 sig.len, &sigc) &&
> -				!sigc.gpg_output)
> +				!sigc.output)
>  				strbuf_addstr(&sig, "gpg verification failed.\n");
>  			else
> -				strbuf_addstr(&sig, sigc.gpg_output);
> +				strbuf_addstr(&sig, sigc.output);

These are "rename some fields for consistency" the proposed log message promised. Makes sense, as you are taking the sigc structure away from pgp/gpg dependency.

Show 9 quoted lines
> diff --git a/gpg-interface.c b/gpg-interface.c
> index 127aecfc2b0..3c9a48c8e7e 100644
> --- a/gpg-interface.c
> +++ b/gpg-interface.c
> @@ -8,6 +8,7 @@
>  #include "tempfile.h"
>  
>  static char *configured_signing_key;
> +const char *ssh_allowed_signers, *ssh_revocation_file;
Very likely these want to be file-scope statics?
Show 10 quoted lines
>  static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
>  
>  struct gpg_format {
> @@ -35,6 +36,14 @@ static const char *x509_sigs[] = {
>  	NULL
>  };
>  
> +static const char *ssh_verify_args[] = {
> +	NULL
> +};
A blank line is missing from here.
Show 39 quoted lines
> +static const char *ssh_sigs[] = {
> +	"-----BEGIN SSH SIGNATURE-----",
> +	NULL
> +};
> +
>  static struct gpg_format gpg_format[] = {
>  	{ .name = "openpgp", .program = "gpg",
>  	  .verify_args = openpgp_verify_args,
> @@ -44,6 +53,9 @@ static struct gpg_format gpg_format[] = {
>  	  .verify_args = x509_verify_args,
>  	  .sigs = x509_sigs
>  	},
> +	{ .name = "ssh", .program = "ssh-keygen",
> +	  .verify_args = ssh_verify_args,
> +	  .sigs = ssh_sigs },
>  };
>  
>  static struct gpg_format *use_format = &gpg_format[0];
> @@ -72,7 +84,7 @@ static struct gpg_format *get_format_by_sig(const char *sig)
>  void signature_check_clear(struct signature_check *sigc)
>  {
>  	FREE_AND_NULL(sigc->payload);
> -	FREE_AND_NULL(sigc->gpg_output);
> +	FREE_AND_NULL(sigc->output);
>  	FREE_AND_NULL(sigc->gpg_status);
>  	FREE_AND_NULL(sigc->signer);
>  	FREE_AND_NULL(sigc->key);
> @@ -257,16 +269,15 @@ error:
>  	FREE_AND_NULL(sigc->key);
>  }
>  
> -static int verify_signed_buffer(const char *payload, size_t payload_size,
> -				const char *signature, size_t signature_size,
> -				struct strbuf *gpg_output,
> -				struct strbuf *gpg_status)
> +static int verify_gpg_signature(struct signature_check *sigc, struct gpg_format *fmt,
> +	const char *payload, size_t payload_size,
> +	const char *signature, size_t signature_size)
>  {

What is this hunk about? The more generic name "verify-signed-buffer" is rescinded and gets replaced by a more GPG/PGP specific helper?

You'd need to help readers a bit more by explaining in the proposed log message that you shifted the boundary of responsibility between check_signature() and verify_signed_buffer()---it used to be that the latter inspected the signed payload to see if it a valid GPG/PGP signature before doing GPG specific validation, but you want to make the former responsible for calling get_format_by_sig(), so that you can dispatch a totally new backend that sits next to this GPG specific one.

Show 30 quoted lines
>  	struct child_process gpg = CHILD_PROCESS_INIT;
> -	struct gpg_format *fmt;
>  	struct tempfile *temp;
>  	int ret;
> -	struct strbuf buf = STRBUF_INIT;
> +	struct strbuf gpg_out = STRBUF_INIT;
> +	struct strbuf gpg_err = STRBUF_INIT;
>  
>  	temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
>  	if (!temp)
> @@ -279,29 +290,28 @@ static 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);
> -
>  	strvec_push(&gpg.args, fmt->program);
>  	strvec_pushv(&gpg.args, fmt->verify_args);
>  	strvec_pushl(&gpg.args,
> -		     "--status-fd=1",
> -		     "--verify", temp->filename.buf, "-",
> -		     NULL);
> -
> -	if (!gpg_status)
> -		gpg_status = &buf;
> +			"--status-fd=1",
> +			"--verify", temp->filename.buf, "-",
> +			NULL);

What is going on around here? Ahh, an unnecessary indentation change is fooling the diff and made the patch unreadable. Sigh...

Show 5 quoted lines
>  	sigchain_push(SIGPIPE, SIG_IGN);
> -	ret = pipe_command(&gpg, payload, payload_size,
> -			   gpg_status, 0, gpg_output, 0);
> +	ret = pipe_command(&gpg, payload, payload_size, &gpg_out, 0,
> +				&gpg_err, 0);

What is this change about? Is it another unnecessary indentation change? Please make sure you keep distraction to your readers to the minimum.

Show 31 quoted lines
> @@ -309,35 +319,36 @@ static int verify_signed_buffer(const char *payload, size_t payload_size,
>  int check_signature(const char *payload, size_t plen, const char *signature,
>  	size_t slen, struct signature_check *sigc)
>  {
> -	struct strbuf gpg_output = STRBUF_INIT;
> -	struct strbuf gpg_status = STRBUF_INIT;
> +	struct gpg_format *fmt;
>  	int status;
>  
>  	sigc->result = 'N';
>  	sigc->trust_level = -1;
>  
> -	status = verify_signed_buffer(payload, plen, signature, slen,
> -				      &gpg_output, &gpg_status);
> -	if (status && !gpg_output.len)
> -		goto out;
> -	sigc->payload = xmemdupz(payload, plen);
> -	sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
> -	sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
> -	parse_gpg_output(sigc);
> +	fmt = get_format_by_sig(signature);
> +	if (!fmt) {
> +		error(_("bad/incompatible signature '%s'"), signature);
> +		return -1;
> +	}
> +
> +	if (!strcmp(fmt->name, "ssh")) {
> +		status = verify_ssh_signature(sigc, fmt, payload, plen, signature, slen);
> +	} else {
> +		status = verify_gpg_signature(sigc, fmt, payload, plen, signature, slen);
> +	}

OK, so get_format_by_sig() now is used to dispatch to the right backend. Which sort of makes sense, but ...

 * "ssh" is the newcomer; it has no right to come before the
   battle-tested existing one.
 * If we are dispatching via "fmt" variable, we should add
   fmt->verify() method to each of these formats, so that we don't
   have to switch based on the name.
IOW, this part should just be
	fmt = get_format_by_sig(signature);
	if (!fmt)
		return error(_("...bad signature..."));
	fmt->verify_signature(sigc, fmt, payload, plen, signature, slen);
Show 5 quoted lines
> +	if (status && !sigc->output)
> +		return !!status;
> +
>  	status |= sigc->result != 'G';
>  	status |= sigc->trust_level < configured_min_trust_level;

By the way, there is no verify_ssh_signature() function defined at this step [1/9], so this won't compile from the source at all. Please make sure that each step builds and passes tests.

If I were doing this patch, I probably would NOT do anything related to "ssh" in this step. Probably just doing

 - rename gpg_* variables to generic names in codepaths that _will_
   become generic in future steps (like "check_signature()"
   function);
 - introduce verify_signature member to the fmt struct;
 - hoist get_format_by_sig()'s callsite to check_signature() from
   its callee.

would be sufficient amount of work for the first step. Call that a preliminary refactoring and clean-up.

And then in the second and subsequent steps, you may start adding additional code to support ssh signing, including the new instance of fmt that has verify_ssh_signature() as its verify_signature method, etc.

Introducing ssh_allowed_signers and ssh_revocation_file at this step is way premature. Nobody uses them in this step, the code that uses them is already referenced but missing (hence the code does not build), so they are only there to frustrate readers wondering what they are for and how they will be used.

Thanks.
Previous: Fabian Stelzer via GitGitGadgetNext: Eric Sunshine
Message 23 of 153 in “Add commit & tag signing/verification via SSH keys using ssh-keygen”
  1. Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 6, 2021
  2. Han-Wen NienhuysJul 6, 2021
  3. Fabian StelzerJul 6, 2021
  4. brian m. carlsonJul 6, 2021
  5. Fabian StelzerJul 6, 2021
  6. Junio C HamanoJul 6, 2021
  7. Fabian StelzerJul 6, 2021
  8. Junio C HamanoJul 6, 2021
  9. Randall S. BeckerJul 6, 2021
  10. Bagas SanjayaJul 7, 2021
  11. Fabian StelzerJul 7, 2021
  12. Add commit, tag & push signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 12, 2021
  13. Ævar Arnfjörð BjarmasonJul 12, 2021
  14. Fabian StelzerJul 12, 2021
  15. Felipe ContrerasJul 12, 2021
  16. 0/9 RFC: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 14, 2021
  17. 2/9 ssh signing: add documentationFabian Stelzer via GitGitGadget, Jul 14, 2021
  18. Junio C HamanoJul 14, 2021
  19. Fabian StelzerJul 15, 2021
  20. Bagas SanjayaJul 15, 2021
  21. Junio C HamanoJul 15, 2021
  22. 1/9 Add commit, tag & push signing via SSH keysFabian Stelzer via GitGitGadget, Jul 14, 2021
  23. Junio C HamanoJul 14, 2021
  24. Eric SunshineJul 14, 2021
  25. Fabian StelzerJul 15, 2021
  26. 3/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Jul 14, 2021
  27. Junio C HamanoJul 14, 2021
  28. Han-Wen NienhuysJul 15, 2021
  29. Fabian StelzerJul 15, 2021
  30. Fabian StelzerJul 15, 2021
  31. 5/9 ssh signing: provide a textual representation of the signing keyFabian Stelzer via GitGitGadget, Jul 14, 2021
  32. 4/9 ssh signing: sign using either gpg or ssh keysFabian Stelzer via GitGitGadget, Jul 14, 2021
  33. Junio C HamanoJul 14, 2021
  34. Fabian StelzerJul 15, 2021
  35. 6/9 ssh signing: parse ssh-keygen output and verify signaturesFabian Stelzer via GitGitGadget, Jul 14, 2021
  36. Gwyneth MorganJul 16, 2021
  37. Fabian StelzerJul 16, 2021
  38. 7/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Jul 14, 2021
  39. 8/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Jul 14, 2021
  40. 9/9 ssh signing: add more tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Jul 14, 2021
  41. 0/9 ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 19, 2021
  42. 1/9 ssh signing: preliminary refactoring and clean-upFabian Stelzer via GitGitGadget, Jul 19, 2021
  43. Junio C HamanoJul 19, 2021
  44. 2/9 ssh signing: add ssh signature format and signing using ssh keysFabian Stelzer via GitGitGadget, Jul 19, 2021
  45. Junio C HamanoJul 19, 2021
  46. Fabian StelzerJul 20, 2021
  47. 3/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Jul 19, 2021
  48. 4/9 ssh signing: provide a textual representation of the signing keyFabian Stelzer via GitGitGadget, Jul 19, 2021
  49. 5/9 ssh signing: parse ssh-keygen output and verify signaturesFabian Stelzer via GitGitGadget, Jul 19, 2021
  50. 6/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Jul 19, 2021
  51. 7/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Jul 19, 2021
  52. 8/9 ssh signing: add more tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Jul 19, 2021
  53. 9/9 ssh signing: add documentationFabian Stelzer via GitGitGadget, Jul 19, 2021
  54. Junio C HamanoJul 20, 2021
  55. 0/9 ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 27, 2021
  56. 1/9 ssh signing: preliminary refactoring and clean-upFabian Stelzer via GitGitGadget, Jul 27, 2021
  57. 2/9 ssh signing: add ssh signature format and signing using ssh keysFabian Stelzer via GitGitGadget, Jul 27, 2021
  58. 4/9 ssh signing: provide a textual representation of the signing keyFabian Stelzer via GitGitGadget, Jul 27, 2021
  59. 3/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Jul 27, 2021
  60. 5/9 ssh signing: parse ssh-keygen output and verify signaturesFabian Stelzer via GitGitGadget, Jul 27, 2021
  61. 6/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Jul 27, 2021
  62. 7/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Jul 27, 2021
  63. 8/9 ssh signing: add more tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Jul 27, 2021
  64. 9/9 ssh signing: add documentationFabian Stelzer via GitGitGadget, Jul 27, 2021
  65. 0/9 ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Jul 28, 2021
  66. 1/9 ssh signing: preliminary refactoring and clean-upFabian Stelzer via GitGitGadget, Jul 28, 2021
  67. Jonathan TanJul 28, 2021
  68. Junio C HamanoJul 29, 2021
  69. Fabian StelzerJul 29, 2021
  70. Fabian StelzerJul 29, 2021
  71. 2/9 ssh signing: add ssh signature format and signing using ssh keysFabian Stelzer via GitGitGadget, Jul 28, 2021
  72. Jonathan TanJul 28, 2021
  73. Junio C HamanoJul 29, 2021
  74. Fabian StelzerJul 29, 2021
  75. Josh SteadmonJul 29, 2021
  76. Fabian StelzerJul 29, 2021
  77. 3/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Jul 28, 2021
  78. Junio C HamanoJul 28, 2021
  79. Jonathan TanJul 28, 2021
  80. Fabian StelzerJul 29, 2021
  81. Josh SteadmonJul 29, 2021
  82. Junio C HamanoJul 29, 2021
  83. Fabian StelzerJul 29, 2021
  84. 5/9 ssh signing: parse ssh-keygen output and verify signaturesFabian Stelzer via GitGitGadget, Jul 28, 2021
  85. Junio C HamanoJul 28, 2021
  86. Fabian StelzerJul 29, 2021
  87. Junio C HamanoJul 29, 2021
  88. Jonathan TanJul 28, 2021
  89. Fabian StelzerJul 29, 2021
  90. Fabian StelzerJul 29, 2021
  91. Fabian StelzerAug 3, 2021
  92. Fabian StelzerAug 3, 2021
  93. Junio C HamanoJul 29, 2021
  94. Randall S. BeckerJul 29, 2021
  95. Fabian StelzerJul 29, 2021
  96. Randall S. BeckerJul 29, 2021
  97. Fabian StelzerJul 29, 2021
  98. Randall S. BeckerJul 29, 2021
  99. Fabian StelzerJul 30, 2021
  100. Randall S. BeckerJul 30, 2021
  101. Fabian StelzerJul 30, 2021
  102. Randall S. BeckerJul 30, 2021
  103. 6/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Jul 28, 2021
  104. Josh SteadmonJul 29, 2021
  105. Junio C HamanoJul 29, 2021
  106. Fabian StelzerJul 30, 2021
  107. 4/9 ssh signing: provide a textual representation of the signing keyFabian Stelzer via GitGitGadget, Jul 28, 2021
  108. Junio C HamanoJul 28, 2021
  109. Fabian StelzerJul 29, 2021
  110. 8/9 ssh signing: add more tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Jul 28, 2021
  111. 9/9 ssh signing: add documentationFabian Stelzer via GitGitGadget, Jul 28, 2021
  112. 7/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Jul 28, 2021
  113. Bagas SanjayaJul 29, 2021
  114. Fabian StelzerJul 29, 2021
  115. 0/9 ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Aug 3, 2021
  116. 1/9 ssh signing: preliminary refactoring and clean-upFabian Stelzer via GitGitGadget, Aug 3, 2021
  117. 2/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Aug 3, 2021
  118. 3/9 ssh signing: add ssh key format and signing codeFabian Stelzer via GitGitGadget, Aug 3, 2021
  119. 4/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Aug 3, 2021
  120. 5/9 ssh signing: provide a textual signing_key_idFabian Stelzer via GitGitGadget, Aug 3, 2021
  121. 7/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Aug 3, 2021
  122. 6/9 ssh signing: verify signatures using ssh-keygenFabian Stelzer via GitGitGadget, Aug 3, 2021
  123. Junio C HamanoAug 3, 2021
  124. Fabian StelzerAug 4, 2021
  125. Junio C HamanoAug 4, 2021
  126. 8/9 ssh signing: tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Aug 3, 2021
  127. 9/9 ssh signing: test that gpg fails for unkown keysFabian Stelzer via GitGitGadget, Aug 3, 2021
  128. Junio C HamanoAug 29, 2021
  129. Gwyneth MorganAug 29, 2021
  130. Fabian StelzerAug 30, 2021
  131. Junio C HamanoSep 7, 2021
  132. Fabian StelzerSep 10, 2021
  133. Junio C HamanoSep 10, 2021
  134. Fabian StelzerSep 10, 2021
  135. Carlo ArenasSep 10, 2021
  136. 0/9 ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygenFabian Stelzer via GitGitGadget, Sep 10, 2021
  137. 1/9 ssh signing: preliminary refactoring and clean-upFabian Stelzer via GitGitGadget, Sep 10, 2021
  138. 2/9 ssh signing: add test prereqsFabian Stelzer via GitGitGadget, Sep 10, 2021
  139. 3/9 ssh signing: add ssh key format and signing codeFabian Stelzer via GitGitGadget, Sep 10, 2021
  140. 4/9 ssh signing: retrieve a default key from ssh-agentFabian Stelzer via GitGitGadget, Sep 10, 2021
  141. 5/9 ssh signing: provide a textual signing_key_idFabian Stelzer via GitGitGadget, Sep 10, 2021
  142. 6/9 ssh signing: verify signatures using ssh-keygenFabian Stelzer via GitGitGadget, Sep 10, 2021
  143. 7/9 ssh signing: duplicate t7510 tests for commitsFabian Stelzer via GitGitGadget, Sep 10, 2021
  144. 8/9 ssh signing: tests for logs, tags & push certsFabian Stelzer via GitGitGadget, Sep 10, 2021
  145. 9/9 ssh signing: test that gpg fails for unknown keysFabian Stelzer via GitGitGadget, Sep 10, 2021
  146. t7510-signed-commit.sh hangs on old gpg, regression in 1bfb57f642d (was: [PATCH v8 9/9] ssh signing: test that gpg fails for unknown keys)Ævar Arnfjörð Bjarmason, Dec 22, 2021
  147. Fabian StelzerDec 22, 2021
  148. brian m. carlsonDec 22, 2021
  149. Ævar Arnfjörð BjarmasonDec 26, 2021
  150. Fabian StelzerDec 30, 2021
  151. Junio C HamanoSep 10, 2021
  152. Fabian StelzerSep 10, 2021
  153. Junio C HamanoSep 10, 2021

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.