Message ID | 7d1d131ff5b43559c8a750ebdfd6faaba93c1ad1.1627501009.git.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ssh signing: Add commit & tag signing/verification via SSH keys using ssh-keygen | expand |
"Fabian Stelzer via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Fabian Stelzer <fs@gigacodes.de> > > for ssh the user.signingkey can be a filename/path or even a literal ssh pubkey. > in push certs and textual output we prefer the ssh fingerprint instead. These sentences that lack the initial capital letters would look unusual and distracting in our "git log --no-merges" stream. > +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; > + > + /* > + * With 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]) > + die_errno(_("failed to get the ssh fingerprint for key '%s'"), > + signing_key); > + > + return strbuf_detach(fingerprint[1], NULL); > +} > + > /* Returns the first public key from an ssh-agent to use for signing */ > static char *get_default_ssh_signing_key(void) > { > @@ -490,6 +525,17 @@ static char *get_default_ssh_signing_key(void) > return ""; > } > > +/* Returns a textual but unique representation ot the signing key */ "ot" -> "of". > +const char *get_signing_key_id(void) > +{ > + if (!strcmp(use_format->name, "ssh")) { > + return get_ssh_key_fingerprint(get_signing_key()); > + } else { > + /* GPG/GPGSM only store a key id on this variable */ > + return get_signing_key(); Hmph, we could ask gpg key fingerprint if we wanted to, and we cannot tell why "ssh" side needs a separate "key" and "key_id" while "gpg" side does not. Hopefully it will become clear as we read on? Again, dispatching on use_format->name looked rather unexpected. > + } > +} > + > const char *get_signing_key(void) > { > if (configured_signing_key) > diff --git a/gpg-interface.h b/gpg-interface.h > index feac4decf8b..beefacbb1e9 100644 > --- a/gpg-interface.h > +++ b/gpg-interface.h > @@ -64,6 +64,12 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, > int git_gpg_config(const char *, const char *, void *); > void set_signing_key(const char *); > const char *get_signing_key(void); > + > +/* > + * Returns a textual unique representation of the signing key in use > + * Either a GPG KeyID or a SSH Key Fingerprint > + */ > +const char *get_signing_key_id(void); > int check_signature(const char *payload, size_t plen, > const char *signature, size_t slen, > struct signature_check *sigc); > diff --git a/send-pack.c b/send-pack.c > index 5a79e0e7110..50cca7e439b 100644 > --- a/send-pack.c > +++ b/send-pack.c > @@ -341,13 +341,13 @@ static int generate_push_cert(struct strbuf *req_buf, > { > const struct ref *ref; > struct string_list_item *item; > - char *signing_key = xstrdup(get_signing_key()); > + char *signing_key_id = xstrdup(get_signing_key_id()); > const char *cp, *np; > struct strbuf cert = STRBUF_INIT; > int update_seen = 0; > > strbuf_addstr(&cert, "certificate version 0.1\n"); > - strbuf_addf(&cert, "pusher %s ", signing_key); > + strbuf_addf(&cert, "pusher %s ", signing_key_id); Ahh... We do not send GPG fingerprint in push certificate but you want to use the fingerprint when signing with SSH keys, and that is where the need for signing_key_id comes from? OK.
On 28.07.21 23:34, Junio C Hamano wrote: > "Fabian Stelzer via GitGitGadget" <gitgitgadget@gmail.com> writes: > >> From: Fabian Stelzer <fs@gigacodes.de> >> >> for ssh the user.signingkey can be a filename/path or even a literal ssh pubkey. >> in push certs and textual output we prefer the ssh fingerprint instead. > > These sentences that lack the initial capital letters would look > unusual and distracting in our "git log --no-merges" stream. > Fixed >> >> +/* Returns a textual but unique representation ot the signing key */ > > "ot" -> "of". > Fixed >> +const char *get_signing_key_id(void) >> +{ >> + if (!strcmp(use_format->name, "ssh")) { >> + return get_ssh_key_fingerprint(get_signing_key()); >> + } else { >> + /* GPG/GPGSM only store a key id on this variable */ >> + return get_signing_key(); > > Hmph, we could ask gpg key fingerprint if we wanted to, and we > cannot tell why "ssh" side needs a separate "key" and "key_id" > while "gpg" side does not. Hopefully it will become clear as we > read on? > > Again, dispatching on use_format->name looked rather unexpected. > i will put the two strcmp(ssh) ifs on my todo list to also replace with a callback function. >> - char *signing_key = xstrdup(get_signing_key()); >> + char *signing_key_id = xstrdup(get_signing_key_id()); >> const char *cp, *np; >> struct strbuf cert = STRBUF_INIT; >> int update_seen = 0; >> >> strbuf_addstr(&cert, "certificate version 0.1\n"); >> - strbuf_addf(&cert, "pusher %s ", signing_key); >> + strbuf_addf(&cert, "pusher %s ", signing_key_id); > > Ahh... We do not send GPG fingerprint in push certificate but you > want to use the fingerprint when signing with SSH keys, and that is > where the need for signing_key_id comes from? > > OK. > Previously the push certs contained the configured user.signingkey as "pusher". For gpg this is usually the key id. (e.g.: ABCDEF01) For ssh signing this can now be a file path which would not make much sense to put into the push cert. I did not use the public ssh key since the file can also contain an encrypted private key, so i would have to ask ssh-keygen for the public key anyway. Since the ssh fingerprint more resembles the gpg key id i used it instead. As far as i understand the actual contents of the "pusher" header is not really relevant for the push-cert. The unique nonce is important but besides that its just a signed text blob. If we don't care about having a local users file path in this header we could drop this commit.
diff --git a/gpg-interface.c b/gpg-interface.c index 3afacb48900..ec48a37b6cc 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -470,6 +470,41 @@ int git_gpg_config(const char *var, const char *value, void *cb) return 0; } +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; + + /* + * With 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]) + die_errno(_("failed to get the ssh fingerprint for key '%s'"), + signing_key); + + return strbuf_detach(fingerprint[1], NULL); +} + /* Returns the first public key from an ssh-agent to use for signing */ static char *get_default_ssh_signing_key(void) { @@ -490,6 +525,17 @@ static char *get_default_ssh_signing_key(void) return ""; } +/* Returns a textual but unique representation ot the signing key */ +const char *get_signing_key_id(void) +{ + if (!strcmp(use_format->name, "ssh")) { + return get_ssh_key_fingerprint(get_signing_key()); + } else { + /* GPG/GPGSM only store a key id on this variable */ + return get_signing_key(); + } +} + const char *get_signing_key(void) { if (configured_signing_key) diff --git a/gpg-interface.h b/gpg-interface.h index feac4decf8b..beefacbb1e9 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -64,6 +64,12 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, int git_gpg_config(const char *, const char *, void *); void set_signing_key(const char *); const char *get_signing_key(void); + +/* + * Returns a textual unique representation of the signing key in use + * Either a GPG KeyID or a SSH Key Fingerprint + */ +const char *get_signing_key_id(void); int check_signature(const char *payload, size_t plen, const char *signature, size_t slen, struct signature_check *sigc); diff --git a/send-pack.c b/send-pack.c index 5a79e0e7110..50cca7e439b 100644 --- a/send-pack.c +++ b/send-pack.c @@ -341,13 +341,13 @@ static int generate_push_cert(struct strbuf *req_buf, { const struct ref *ref; struct string_list_item *item; - char *signing_key = xstrdup(get_signing_key()); + char *signing_key_id = xstrdup(get_signing_key_id()); const char *cp, *np; struct strbuf cert = STRBUF_INIT; int update_seen = 0; strbuf_addstr(&cert, "certificate version 0.1\n"); - strbuf_addf(&cert, "pusher %s ", signing_key); + strbuf_addf(&cert, "pusher %s ", signing_key_id); datestamp(&cert); strbuf_addch(&cert, '\n'); if (args->url && *args->url) { @@ -374,7 +374,7 @@ static int generate_push_cert(struct strbuf *req_buf, if (!update_seen) goto free_return; - if (sign_buffer(&cert, &cert, signing_key)) + if (sign_buffer(&cert, &cert, get_signing_key())) die(_("failed to sign the push certificate")); packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string); @@ -386,7 +386,7 @@ static int generate_push_cert(struct strbuf *req_buf, packet_buf_write(req_buf, "push-cert-end\n"); free_return: - free(signing_key); + free(signing_key_id); strbuf_release(&cert); return update_seen; }