Git development
 help / color / mirror / Atom feed
From: Phillip Wood <phillip.wood123@gmail.com>
To: "Christian Noé Ramos López" <chris@nortesoftware.dev>,
	phillip.wood@dunelm.org.uk
Cc: Patrick Steinhardt <ps@pks.im>, Git Mailing List <git@vger.kernel.org>
Subject: Re: ssh signing: valid-before is checked at the signer's own date, and a missing revocationFile fails open
Date: Fri, 9 Oct 2026 14:29:27 +0100	[thread overview]
Message-ID: <6e921d1d-b7d8-4f42-add3-67931e4ffba8@gmail.com> (raw)
In-Reply-To: <CAHGSfba9Td+Lg=Nf+nfwZY2r1_eq2Mcejn__6X=VPQkmcJttfQ@mail.gmail.com>

Hi Chirstian

I've add back the the mailing list cc so others can comment as well.

On 08/10/2026 20:40, Christian Noé Ramos López wrote:
> 
>> Having waded through this here is a human readable summary:
> 
> Fair -- your summary is the shape I should have sent. Next time.
> 
> (1) You are right, and I did not test it. gpg takes the signature's own
> creation time, so a manipulated clock moves that too. What I measured is
> narrower: backdating the committer and author dates does not move it --
> ssh-backdated is accepted, gpg-backdated is refused. A difference in cost,
> not in kind.
> 
> So the wording should say two things: that the time compared against
> valid-before comes from the commit, and that a signature timestamp is not
> evidence of when the signing happened. That covers both backends. I will
> send that patch.

That sounds sensible - a valid signature doesn't tell us anything about 
when the commit was signed.

> (2) One thing before I write it. Failing closed changes behaviour for
> anyone whose configured path is already wrong, from a warning to a failed
> verification. I think that is the right trade, but it is a behaviour
> change and not only a fix. There is no test for gpg.ssh.revocationFile
> today, so the patch should add one either way.

A test would be very welcome. Patrick had a good suggestion for allowing 
the path to be optional if the user wanted.

Thanks

Phillip

> Christian Ramos
> Norte Software
> chris@nortesoftware.dev
> 
> 
> El jue, 8 oct 2026 a la(s) 7:42 a.m., Phillip Wood
> (phillip.wood123@gmail.com) escribió:
>>
>> Hi Christian
>>
>> Having waded through this here is a human readable summary:
>>
>> (1) Our documentation implies that we check the expiry date of the key
>> (which is recorded in the allowed signers file) against the date the
>> commit was signed, but we actually use the committer date which can
>> easily be faked.
>>
>> (2) If the revocation file does not exist we print a warning rather than
>> failing the operation like the gpg backend does.
>>
>> For (1) I'd be happy to see a patch that tightens the wording, but we
>> should also note that the timestamp in the gpg signature can also be faked.
>>
>> For (2) I agree failing seems like the safer option.
>>
>> Thanks
>>
>> Phillip
>>
>> On 08/10/2026 07:56, Christian Noé Ramos López wrote:
>>> Two things that, together, mean an SSH signing key cannot be reliably
>>> stopped from being trusted. git 2.47.3, OpenSSH 10.0p2, Debian 13;
>>> source read at v2.47.3 and at master (c46c1e37724f).
>>>
>>> 1. valid-before is checked at a date the signer writes.
>>>
>>> SSH signatures carry no time of their own, so git passes -Overify-time
>>> from the committer or tagger line (gpg-interface.c,
>>> parse_payload_metadata). alice's key is in the allowed signers file
>>> with valid-before="20260101":
>>>
>>>       ssh-old        %G?=G 2025-06-01 12:00:00 +0000 verify-commit=0 merge=0
>>>       ssh-backdated  %G?=G 2025-06-01 12:00:00 +0000 verify-commit=0 merge=0
>>>       ssh-honest     %G?=U 2026-10-08 02:15:15 -0400 verify-commit=1 merge=128
>>>
>>> ssh-backdated was signed today, with only the committer and author
>>> dates set to 2025-06-01. Nothing distinguishes it from ssh-old except
>>> when it was made, which only its author knows. ssh-honest, signed and
>>> dated today, is refused: "key has expired: verify time ... >
>>> valid-before 2026-01-01T00:00:00".
>>>
>>> The GPG backend refuses both:
>>>
>>>       gpg-old        %G?=Y verify-commit=1 merge=128
>>>       gpg-backdated  %G?=Y verify-commit=1 merge=128
>>>
>>> The documentation for gpg.ssh.allowedSignersFile says "Git will mark
>>> signatures as valid if the signing key was valid at the time of the
>>> signature's creation", which is the intent, but does not say the time
>>> comes from the commit. So valid-before rotates a key; it does not
>>> retire one.
>>>
>>> 2. A configured revocation file that does not exist fails open.
>>>
>>> gpg-interface.c:568-574 at v2.47.3 (579-586 at master): if the
>>> revocation file exists, pass -r; otherwise warn and verify without it.
>>> The same file, present and listing alice's key, refuses:
>>>
>>>       S2-revoked       %G?=B verify-commit=1 merged=no
>>>       S3-revfile-gone  %G?=G verify-commit=0 merged=yes
>>>                        warning: ssh signing revocation file configured
>>> but not found
>>>
>>> S3 merged under `git merge --ff-only --verify-signatures`. An
>>> unreadable file and a directory both fail closed:
>>>
>>>       S4-revfile-0000  %G?=B verify-commit=1 merged=no
>>>       S6-revfile-dir   %G?=B verify-commit=1 merged=no
>>>
>>> ssh-keygen, given the same missing path, refuses: exit 255, "Could not
>>> verify signature". git avoids that by not passing -r. OpenSSH's
>>> RevokedKeys says, in sshd_config(5), "Note that if this file is not
>>> readable, then public key authentication will be refused for all
>>> users."
>>>
>>> No test in git exercises gpg.ssh.revocationFile; it appears only in
>>> Documentation/config/gpg.adoc and gpg-interface.c.
>>>
>>> Controls for both runs, fixed beforehand: a good signature gives G and
>>> merges; an unsigned commit gives N and is refused; the revocation file
>>> present and listing the key gives B and is refused; a commit with its
>>> message changed and the signature kept gives B and is refused.
>>>
>>> Together: the two ways to stop trusting an SSH signing key are
>>> valid-before, which the signer can date around, and revocationFile,
>>> which does nothing if its path is wrong. Either would be enough on its
>>> own if it held.
>>>
>>> What I would ask for: refuse when the revocation file is configured and
>>> missing, as ssh-keygen and sshd do, or say in the documentation that it
>>> is ignored; and say, under valid-before, where the time compared
>>> against it comes from.
>>>
>>> On prior art: the ssh signing series (Fabian Stelzer, 2021) carried the
>>> warning from before v4, and the review raised the config name's case,
>>> not what a missing file should do. The key-lifetime series (RFC
>>> 2021-10-15 to v6 2021-12-09) passes the commit date to the check, and
>>> the replies are about style. The N for an unconfigured allowed signers
>>> file is already on the list (Grayson Tinker, 2026-06-25) and is not
>>> part of this.
>>>
>>> Christian Ramos
>>> Norte Software
>>> chris@nortesoftware.dev
>>
> 
> 


      parent reply	other threads:[~2026-10-09 13:29 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08  6:56 ssh signing: valid-before is checked at the signer's own date, and a missing revocationFile fails open Christian Noé Ramos López
2026-10-08 13:42 ` Phillip Wood
2026-10-09 12:19   ` Patrick Steinhardt
2026-10-09 13:30     ` Phillip Wood
     [not found]   ` <CAHGSfba9Td+Lg=Nf+nfwZY2r1_eq2Mcejn__6X=VPQkmcJttfQ@mail.gmail.com>
2026-10-09 13:29     ` Phillip Wood [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6e921d1d-b7d8-4f42-add3-67931e4ffba8@gmail.com \
    --to=phillip.wood123@gmail.com \
    --cc=chris@nortesoftware.dev \
    --cc=git@vger.kernel.org \
    --cc=phillip.wood@dunelm.org.uk \
    --cc=ps@pks.im \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox