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

Re: [PATCH v2] Add commit, tag & push signing/verification via SSH keys using ssh-keygen

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Jul 12, 2021, 16:55 UTC
Message-ID
<87y2ab30yr.fsf@evledraar.gmail.com>
In-Reply-To
<pull.1041.v2.git.git.1626092359713.gitgitgadget@gmail.com>
On Mon, Jul 12 2021, Fabian Stelzer via GitGitGadget wrote:
Show 82 quoted lines
>  gpg.format::
>  	Specifies which key format to use when signing with `--gpg-sign`.
> -	Default is "openpgp" and another possible value is "x509".
> +	Default is "openpgp". Other possible values are "x509", "ssh".
>  
>  gpg.<format>.program::
>  	Use this to customize the program used for the signing format you
>  	chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
>  	be used as a legacy synonym for `gpg.openpgp.program`. The default
> -	value for `gpg.x509.program` is "gpgsm".
> +	value for `gpg.x509.program` is "gpgsm" and `gpg.ssh.program` is "ssh-keygen".
>  
>  gpg.minTrustLevel::
>  	Specifies a minimum trust level for signature verification.  If
> @@ -33,3 +33,34 @@ gpg.minTrustLevel::
>  * `marginal`
>  * `fully`
>  * `ultimate`
> +
> +gpg.ssh.keyring::
> +	A file containing all valid SSH public signing keys. 
> +	Similar to an .ssh/authorized_keys file.
> +	See ssh-keygen(1) "ALLOWED SIGNERS" for details.
> +	If a signing key is found in this file then the trust level will
> +	be set to "fully". Otherwise if the key is not present
> +	but the signature is still valid then the trust level will be "undefined".
> +
> +	This file can be set to a location outside of the repository
> +	and every developer maintains their own trust store.
> +	A central repository server could generate this file automatically
> +	from ssh keys with push	access to verify the code against.
> +	In a corporate setting this file is probably generated at a global location
> +	from some automation that already handles developer ssh keys. 
> +	
> +	A repository that is only allowing signed commits can store the file 
> +	in the repository itself using a relative path. This way only committers
> +	with an already valid key can add or change keys in the keyring.
> +
> +	Using a SSH CA key with the cert-authority option 
> +	(see ssh-keygen(1) "CERTIFICATES") is also valid.
> +
> +	To revoke a key place the public key without the principal into the 
> +	revocationKeyring.
> +
> +gpg.ssh.revocationKeyring::
> +	Either a SSH KRL or a list of revoked public keys (without the principal prefix).
> +	See ssh-keygen(1) for details.
> +	If a public key is found in this file then it will always be treated
> +	as having trust level "never" and signatures will show as invalid.
> diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt
> index 59aec7c3aed..e71a099b8b8 100644
> --- a/Documentation/config/user.txt
> +++ b/Documentation/config/user.txt
> @@ -36,3 +36,9 @@ user.signingKey::
>  	commit, you can override the default selection with this variable.
>  	This option is passed unchanged to gpg's --local-user parameter,
>  	so you may specify a key using any method that gpg supports.
> +	If gpg.format is set to "ssh" this can contain the literal ssh public
> +	key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and 
> +	corresponds to the private key used for signing. The private key 
> +	needs to be available via ssh-agent. Alternatively it can be set to
> +	a file containing a private key directly. If not set git will call 
> +	"ssh-add -L" and try to use the first key available.
> diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
> index a34742513ac..fd790f7fd72 100644
> --- a/builtin/receive-pack.c
> +++ b/builtin/receive-pack.c
> @@ -131,6 +131,8 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
>  {
>  	int status = parse_hide_refs_config(var, value, "receive");
>  
> +	git_gpg_config(var, value, NULL);
> +
>  	if (status)
>  		return status;
>  
> @@ -767,7 +769,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
>  		bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
>  		check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
>  				push_cert.len - bogs, &sigcheck);
> -
> +		
Stray whitespace change.
Show 10 quoted lines
> +static void parse_ssh_output(struct signature_check *sigc)
> +{
> +	const char *output = NULL;
> +	char *next = NULL;
> +
> +	/* ssh-keysign output should be:
> +	 * Good "git" signature for PRINCIPAL with RSA key SHA256:FINGERPRINT
> +	 * or for valid but unknown keys:
> +	 * Good "git" signature with RSA key SHA256:FINGERPRINT
> +	 */
Style:
 /*
  * Comments like this
  */
Not /* Comments [...]
> +
> +	output = xmemdupz(sigc->output, strcspn(sigc->output, " \n"));
> +	if (skip_prefix(sigc->output, "Good \"git\" signature for ", &output)) {
> +		// Valid signature for a trusted signer

We don't use C99 comments, so /* ... */ (but perhaps we should nowadays, but that's another topic...).

Show 12 quoted lines
> +		sigc->result = 'G';
> +		sigc->trust_level = TRUST_FULLY;
> +
> +		next = strchrnul(output, ' '); // 'principal'
> +		replace_cstring(&sigc->signer, output, next);
> +		output = next + 1;
> +		next = strchrnul(output, ' '); // 'with'
> +		output = next + 1;
> +		next = strchrnul(output, ' '); // KEY Type
> +		output = next + 1;
> +		next = strchrnul(output, ' '); // 'key'
> +		output = next + 1;

FWIW for new code we'd probably use string_list_split() or string_list_split_in_place() or strbuf_split_buf() or something, but I see this is following the existing pattern in the file...

Show 34 quoted lines
> +		next = strchrnul(output, '\n'); // key
>
> +		replace_cstring(&sigc->fingerprint, output, next);
> +		replace_cstring(&sigc->key, output, next);
> +	} else if (skip_prefix(sigc->output, "Good \"git\" signature with ", &output)) {
> +		// Valid signature, but key unknown
> +		sigc->result = 'G';
> +		sigc->trust_level = TRUST_UNDEFINED;
> +
> +		next = strchrnul(output, ' '); // KEY Type
> +		output = next + 1;
> +		next = strchrnul(output, ' '); // 'key'
> +		output = next + 1;
> +		next = strchrnul(output, '\n'); // key
> +		replace_cstring(&sigc->fingerprint, output, next);
> +		replace_cstring(&sigc->key, output, next);
> +	} else {
> +		sigc->result = 'B';
> +		sigc->trust_level = TRUST_NEVER;
> +	}
> +}
> +
>  static void parse_gpg_output(struct signature_check *sigc)
>  {
>  	const char *buf = sigc->gpg_status;
> @@ -257,16 +318,18 @@ 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_ssh_signature(struct signature_check *sigc, struct gpg_format *fmt,
We usually wrap at 80 characters, so since you're wrapping anyway...
Show 46 quoted lines
> +	const char *payload, size_t payload_size,
> +	const char *signature, size_t signature_size)
>  {
> -	struct child_process gpg = CHILD_PROCESS_INIT;
> -	struct gpg_format *fmt;
> +	struct child_process ssh_keygen = CHILD_PROCESS_INIT;
>  	struct tempfile *temp;
>  	int ret;
> -	struct strbuf buf = STRBUF_INIT;
> +	const char *line;
> +	size_t trust_size;
> +	char *principal;
> +	struct strbuf ssh_keygen_out = STRBUF_INIT;
> +	struct strbuf ssh_keygen_err = STRBUF_INIT;
>  
>  	temp = mks_tempfile_t(".git_vtag_tmpXXXXXX");
>  	if (!temp)
> @@ -279,29 +342,125 @@ 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);
> +	// Find the principal from the  signers
> +	strvec_pushl(&ssh_keygen.args,  fmt->program,
> +					"-Y", "find-principals",
> +					"-f", get_ssh_allowed_signers(),
> +					"-s", temp->filename.buf,
> +					NULL);
> +	ret = pipe_command(&ssh_keygen, NULL, 0, &ssh_keygen_out, 0, &ssh_keygen_err, 0);
> +	if (strstr(ssh_keygen_err.buf, "unknown option")) {
> +		error(_("openssh version > 8.2p1 is needed for ssh signature verification (ssh-keygen needs -Y find-principals/verify option)"));
> +	}
> +	if (ret || !ssh_keygen_out.len) {
> +		// We did not find a matching principal in the keyring - Check without validation
> +		child_process_init(&ssh_keygen);
> +		strvec_pushl(&ssh_keygen.args,  fmt->program,
> +						"-Y", "check-novalidate",
> +						"-n", "git",
> +						"-s", temp->filename.buf,
> +						NULL);
> +		ret = pipe_command(&ssh_keygen, payload, payload_size, &ssh_keygen_out, 0, &ssh_keygen_err, 0);
> +	} else {
> +		// Check every principal we found (one per line)
> +		for (line = ssh_keygen_out.buf; *line; line = strchrnul(line + 1, '\n')) {

Hrm, can't we use strbuf_getline() here with the underlying io_pump API that pipe_command() uses, instead of slurping it all up, and then splitting on '\n' ourselves? (I'm not sure)

Show 23 quoted lines
> +			while (*line == '\n')
> +				line++;
> +			if (!*line)
> +				break;
> +
> +			trust_size = strcspn(line, " \n");
> +			principal = xmemdupz(line, trust_size);
> +
> +			child_process_init(&ssh_keygen);
> +			strbuf_release(&ssh_keygen_out);
> +			strbuf_release(&ssh_keygen_err);
> +			strvec_push(&ssh_keygen.args,fmt->program);
> +			// We found principals - Try with each until we find a match
> +			strvec_pushl(&ssh_keygen.args,  "-Y", "verify",
> +							//TODO: sprintf("-Overify-time=%s", commit->date...),
> +							"-n", "git",
> +							"-f", get_ssh_allowed_signers(),
> +							"-I", principal,
> +							"-s", temp->filename.buf,
> +							NULL);
> +
> +			if (ssh_revocation_file && file_exists(ssh_revocation_file)) {
> +				strvec_pushl(&ssh_keygen.args, "-r", ssh_revocation_file, NULL);
Do we want to silently ignore missing but configured revocation files?
Show 20 quoted lines
> +			}
> +
> +			sigchain_push(SIGPIPE, SIG_IGN);
> +			ret = pipe_command(&ssh_keygen, payload, payload_size,
> +					&ssh_keygen_out, 0, &ssh_keygen_err, 0);
> +			sigchain_pop(SIGPIPE);
> +
> +			ret &= starts_with(ssh_keygen_out.buf, "Good");
> +			if (ret == 0)
> +				break;
> +		}
> +	}
> +
> +	sigc->payload = xmemdupz(payload, payload_size);
> +	strbuf_stripspace(&ssh_keygen_out, 0);
> +	strbuf_stripspace(&ssh_keygen_err, 0);
> +	strbuf_add(&ssh_keygen_out, ssh_keygen_err.buf, ssh_keygen_err.len);
> +	sigc->output = strbuf_detach(&ssh_keygen_out, NULL);
> +
> +	//sigc->gpg_output = strbuf_detach(&ssh_keygen_err, NULL); // This flip around is broken...
Broken how? And why the commented-out code as part of the patch?
Show 11 quoted lines
> -	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)
> +		BUG("bad signature '%s'", signature);

So if we run this from receive-pack or whatever we'll BUG() out? I.e. I think this should be an fsck check or something, but not a BUG(), or does this not rely on potentially bad object-store state?

Show 28 quoted lines
> +static char *get_ssh_key_fingerprint(const char *signing_key) {
> +	struct child_process ssh_keygen = CHILD_PROCESS_INIT;
> +	int ret = -1;
> +	struct strbuf fingerprint_stdout = STRBUF_INIT;
> +	struct strbuf **fingerprint;
> +
> +	/* For SSH Signing this can contain a filename or a public key
> +	* For textual representation we usually want a fingerprint
> +	*/
> +	if (istarts_with(signing_key, "ssh-")) {
> +		strvec_pushl(&ssh_keygen.args, "ssh-keygen",
> +					"-lf", "-",
> +					NULL);
> +		ret = pipe_command(&ssh_keygen, signing_key, strlen(signing_key), &fingerprint_stdout, 0,  NULL, 0);
> +	} else {
> +		strvec_pushl(&ssh_keygen.args, "ssh-keygen",
> +					"-lf", configured_signing_key,
> +					NULL);
> +		ret = pipe_command(&ssh_keygen, NULL, 0, &fingerprint_stdout, 0, NULL, 0);
> +		if (!!ret)
> +			die_errno(_("failed to get the ssh fingerprint for key '%s'"), signing_key);
> +		fingerprint = strbuf_split_max(&fingerprint_stdout, ' ', 3);
> +		if (fingerprint[1]) {
> +			return strbuf_detach(fingerprint[1], NULL);
> +		}
> +	}
> +	die_errno(_("failed to get the ssh fingerprint for key '%s'"), signing_key);
> +}

Her you declare a ret that's not used at all in the "istarts_with" branch, and we fall through to die_errno()?

[I stopped reading mostly at this point]
Show 28 quoted lines
> [...]
> +# test_expect_success GPGSSH 'detect fudged commit with double signature' '
> +# 	sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
> +# 	sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
> +# 		sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
> +# 	gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
> +# 	cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
> +# 	sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
> +# 		double-combined.asc > double-gpgsig &&
> +# 	sed -e "/committer/r double-gpgsig" double-base >double-commit &&
> +# 	git hash-object -w -t commit double-commit >double-commit.commit &&
> +# 	test_must_fail git verify-commit $(cat double-commit.commit) &&
> +# 	git show --pretty=short --show-signature $(cat double-commit.commit) >double-actual &&
> +# 	grep "BAD signature from" double-actual &&
> +# 	grep "Good signature from" double-actual
> +# '
> +
> +# test_expect_success GPGSSH 'show double signature with custom format' '
> +# 	cat >expect <<-\EOF &&
> +# 	E
> +
> +
> +
> +
> +# 	EOF
> +# 	git log -1 --format="%G?%n%GK%n%GS%n%GF%n%GP" $(cat double-commit.commit) >actual &&
> +# 	test_cmp expect actual
> +# '
Perhaps you're looking for test_expect_failure for TODO tests?

I think this patch is *way* past the point of benefitting from being split into a patch series. It grew from ~200 lines added to ~1k.

Previous: Fabian Stelzer via GitGitGadgetNext: Fabian Stelzer
Message 13 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.