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

Re: [PATCH v4 2/9] ssh signing: add ssh signature format and signing using ssh keys

From
Fabian Stelzer <fs@gigacodes.de>
Date
Jul 20, 2021, 12:26 UTC
Message-ID
<b673b68a-6525-44fd-0c1b-7943eb794184@gigacodes.de>
In-Reply-To
<xmqqpmvdj1xp.fsf@gitster.g>
On 20.07.21 01:53, Junio C Hamano wrote:
Show 21 quoted lines
> "Fabian Stelzer via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> @@ -65,6 +73,14 @@ static struct gpg_format gpg_format[] = {
>>   		.verify_signed_buffer = verify_gpg_signed_buffer,
>>   		.sign_buffer = sign_buffer_gpg,
>>   	},
>> +	{
>> +		.name = "ssh",
>> +		.program = "ssh-keygen",
>> +		.verify_args = ssh_verify_args,
>> +		.sigs = ssh_sigs,
>> +		.verify_signed_buffer = NULL, /* TODO */
>> +		.sign_buffer = sign_buffer_ssh
>> +	},
>>   };
> A payload a malicious person may feed this version of Git can have a
> pattern that happens to match the ssh_sigs[] string, and the code
> will blindly try to call .verify_signed_buffer==NULL and die, no?
>
> That is not the end of the world; as long as we know that with the
> above "TODO" comment it is probably OK.

I thought about adding an if(!fmt->sign) BUG() but since these callbacks are static it shoud really only be an issue between patches of the set.

Show 20 quoted lines
>
>> @@ -463,12 +482,26 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
>>   	return use_format->sign_buffer(buffer, signature, signing_key);
>>   }
>>   
>> +static void strbuf_trim_trailing_cr(struct strbuf *buffer, int offset)
>> +{
> This removes any and all CR, not just trimming the trailing ones, so
> the function is misnamed. Call it remove_cr_after() perhaps?
>
> Alternatively we could tighten the implementation and strip only the
> CR that come immediately before a LF.  That would be a better longer
> term thing to do, but because you are lifting an existing code from
> the end of the gpg side of the thing, it may make sense to keep the
> implementation as-is, but give it a name that is more faithful to
> what it actually does.  When the dust settles, we may want to
> revisit and fix this helper function to actually trim CRLF into LF
> (and leave CR in the middle of lines intact), but I do not think it
> is urgent.  Just leaving "NEEDSWORK: make it trim only CRs before LFs
> and rename" comment would be OK.
Agreed. I've renamed it and added the comment.
>
> Shouldn't the offset (aka bottom) be of type size_t?
fixed
Show 21 quoted lines
>> +static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
>> +			   const char *signing_key)
>> +{
>> +	struct child_process signer = CHILD_PROCESS_INIT;
>> +	int ret = -1;
>> +	size_t bottom;
>> +	struct strbuf signer_stderr = STRBUF_INIT;
>> +	struct tempfile *temp = NULL, *buffer_file = NULL;
>> +	char *ssh_signing_key_file = NULL;
>> +	struct strbuf ssh_signature_filename = STRBUF_INIT;
>> +
>> +	if (!signing_key || signing_key[0] == '\0')
>> +		return error(
>> +			_("user.signingkey needs to be set for ssh signing"));
>> +
>> +	if (istarts_with(signing_key, "ssh-")) {
> Is it common in the ssh world to treat ssh- prefix as case
> insensitive?  Not a strong objection but I tend to prefer to start
> strict unless there is a good reason to be loose when we do not have
> to, as loosening after the fact is much easier than tightening after
> starting with a loose definition.
I don't think so. I will make it case sensitive.
Show 12 quoted lines
>
>> +		/* A literal ssh key */
>> +		temp = mks_tempfile_t(".git_signing_key_tmpXXXXXX");
>> +		if (!temp)
>> +			return error_errno(
>> +				_("could not create temporary file"));
>> +		if (write_in_full(temp->fd, signing_key, strlen(signing_key)) <
>> +			    0 ||
> "keylen = strlen(signing_key)" before that line, for example, could
> have easily avoided the line-wrapping at such a place.  Wrapping at
> places like after ||, i.e. after an operator with a low precedence,
> would make the code easier to follow.
agreed.
Show 19 quoted lines
>
>> +		    close_tempfile_gently(temp) < 0) {
>> +			error_errno(_("failed writing ssh signing key to '%s'"),
>> +				    temp->filename.buf);
>> +			goto out;
>> +		}
>> +		ssh_signing_key_file = temp->filename.buf;
> It is kind'a sad that we need a fresh temporary file every time, but
> we can easily tell the user in the documentation that they can use a
> file with a key in it to avoid it, so it's OK (actually, better than
> OK, as without this, we may not consume temporary files but we won't
> offer an ability to take a literal key string).
>
> Is ".git_whatever file in the current directory" a good place to
> have this temporary file?  I would have expected that we would use
> either $GIT_DIR, $HOME, or $TMPDIR for a thing like this (with
> different pros-and-cons discussion).  At least it is consistent with
> how a temporary file for the payload to be sign-verified is created,
> so let's leave it as-is.

Intuitively i thought just using mks_tempfile_t() would choose a good dir for such files.

Show 23 quoted lines
>
>> +	} else {
>> +		/* We assume a file */
>> +		ssh_signing_key_file = expand_user_path(signing_key, 1);
>> +	}
>> +
>> +	buffer_file = mks_tempfile_t(".git_signing_buffer_tmpXXXXXX");
>> +	if (!buffer_file) {
>> +		error_errno(_("could not create temporary file"));
>> +		goto out;
>> +	}
>> +
>> +	if (write_in_full(buffer_file->fd, buffer->buf, buffer->len) < 0 ||
>> +	    close_tempfile_gently(buffer_file) < 0) {
>> +		error_errno(_("failed writing ssh signing key buffer to '%s'"),
>> +			    buffer_file->filename.buf);
>> +		goto out;
>> +	}
>> +
>> +	strvec_pushl(&signer.args, use_format->program, "-Y", "sign", "-n",
>> +		     "git", "-f", ssh_signing_key_file,
> Wrap the line before "-n" to keep "-n" and "git" together, if "git"
> is meant as an argument to the "-n" option.

done. some things clang-format can't really understand. overall it is quite helpful but a few things i still had to reformat.

Show 34 quoted lines
>
>> +		     buffer_file->filename.buf, NULL);
>> +
>> +	sigchain_push(SIGPIPE, SIG_IGN);
>> +	ret = pipe_command(&signer, NULL, 0, NULL, 0, &signer_stderr, 0);
>> +	sigchain_pop(SIGPIPE);
>> +
>> +	if (ret && strstr(signer_stderr.buf, "usage:")) {
>> +		error(_("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)"));
>> +		goto out;
> This error message is important to give to the end users, but is it
> enough?  That is, unless "usage:" does not appear, we show the whole
> raw error message and that would help end users and those helping
> them to diagnose the issue, but once the underlying program says
> "usage:", no matter what else it says, it is hidden by this code,
> since we assume it is a wrong version of openssh.
>
>> +	}
>> +
>> +	if (ret) {
>> +		error("%s", signer_stderr.buf);
>> +		goto out;
>> +	}
> Also, prehaps
>
> 	if (ret) {
> 		if (strstr(..., "usage"))
> 			error(_("ssh-keygen -Y sign is needed..."));
> 		else
>                          error("%s", signer_stderr.buf);
> 		goto out;
> 	}
>
> would be easier to follow.
I have changed this to:
if (ret) {
     if (strstr(..., "usage"))
         error(_("ssh-keygen -Y sign is needed..."));
     error("%s", signer_stderr.buf);
     goto out;
}
and removed the if (ret) further down in the function that had no effect.

I had removed the raw stderr output from verify & sign because it becomes quite unreadable when doing a "git log --show-signature" with ssh signatures present and not support in ssh for it. I think in case of signing the full output is good. The user is taking an active action (wanting to sign something) so we should give them all the help we can when things go wrong. The output for verification is debatable. The config might have a "log.showSignature" for verifying gpg signatures and when ssh signatures show up we should tell them it's not supported on their setup but probably not showing endless lines of errors when they do a "git log". A "git verify-commit" might be a different case. But code-path-wise this is the same thing at the moment.

Show 16 quoted lines
>> +		error(_("ssh failed to sign the data"));
>> +		goto out;
>> +	}
>> +
>> +	/* Strip CR from the line endings, in case we are on Windows. */
>> +	strbuf_trim_trailing_cr(signature, bottom);
>> +
>> +out:
>> +	if (temp)
>> +		delete_tempfile(&temp);
>> +	if (buffer_file)
>> +		delete_tempfile(&buffer_file);
> It is clear that the latter one was holding the contents of the
> buffer to be signed, but reminding the readers what "temp" was about
> would be a good move.  Perhaps renaming the variable to "key_file"
> or something may help?
renamed to "key_file"
Show 10 quoted lines
>
>> +	strbuf_release(&signer_stderr);
>> +	strbuf_release(&ssh_signature_filename);
>> +	return ret;
>> +}
> Looking good, except for the "when does 'ret' get updated?
> shouldn't we refrain from reading the resulting buffer when it is
> set?" question.
>
> Thanks for a pleasant read.
Thanks a lot for your support with this!
>
Previous: Junio C HamanoNext: Fabian Stelzer via GitGitGadget
Message 46 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.