From: Phillip Wood Date: Fri, 09 Oct 2026 13:29:27 GMT Subject: Re: ssh signing: valid-before is checked at the signer's own date, and a missing revocationFile fails open Message-ID: <6e921d1d-b7d8-4f42-add3-67931e4ffba8@gmail.com> In-Reply-To: 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 >> > >