Skip to content

git: allow signing commits with SSH key#1222

Merged
matheuscscp merged 12 commits into
mainfrom
git-ssh-signing
Jun 5, 2026
Merged

git: allow signing commits with SSH key#1222
matheuscscp merged 12 commits into
mainfrom
git-ssh-signing

Conversation

@hiddeco

@hiddeco hiddeco commented May 29, 2026

Copy link
Copy Markdown
Member

Builds on top of #1141

@stefanprodan

Copy link
Copy Markdown
Member

@hiddeco can you please rebase this PR

@hiddeco hiddeco force-pushed the git-ssh-signing branch 2 times, most recently from 060b313 to 7c001f1 Compare June 5, 2026 13:39
@hiddeco hiddeco marked this pull request as ready for review June 5, 2026 13:40
@hiddeco hiddeco requested a review from a team as a code owner June 5, 2026 13:40

@matheuscscp matheuscscp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🚀

@stefanprodan stefanprodan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Thanks @hiddeco 🏅

@stefanprodan stefanprodan added area/git Git and SSH related issues and pull requests area/security Security related issues and pull requests labels Jun 5, 2026
hiddeco added 12 commits June 5, 2026 17:10
Re-exports the go-git Signer interface from the signature package so
consumers can refer to it without importing go-git directly. Prepares
for the upcoming NewOpenPGPSigner and NewSSHSigner constructors.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Wraps an *openpgp.Entity in a Signer so it can be used through the new
generic signing path on CommitOptions. Produces the same ASCII-armored
detached signature as go-git's internal gpgSigner, preserving existing
commit-signing output bit-for-bit.

The concrete OpenPGPSigner type is exported so callers can type-assert
a Signer returned by NewOpenPGPSigner and distinguish it from other
Signer implementations.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Wraps an SSH private key in a Signer that produces SSHSIG-armored
signatures with namespace "git" and SHA-512 hash, matching Git's
defaults for SSH-signed commits.

The concrete SSHSigner type is exported so callers can type-assert a
Signer returned by NewSSHSigner. This commit covers the unencrypted-
key happy path; encrypted keys and the algorithm allowlist land in
follow-up commits.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
OpenSSH-format private keys cannot be classified as encrypted before
attempting to parse. Detect *gossh.PassphraseMissingError, require a
passphrase, and retry through ParsePrivateKeyWithPassphrase. Wrong-
passphrase parse failures surface with the generic "could not parse"
wrap; missing-passphrase errors are explicit.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Allowlist ssh-ed25519, ecdsa-sha2-nistp256/384/521, and ssh-rsa with
key size at least 2048 bits. DSA and undersized RSA produce signatures
modern OpenSSH refuses to verify, so reject them at construction time
instead of letting downstream verify silently fail.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
CommitOptions.Signer changes from *openpgp.Entity to the go-git Signer
interface re-exported from the signature package, and WithSigner takes
the same type. The gogit client forwards the value to go-git's generic
Signer field, which takes precedence over the legacy typed SignKey.

This is a breaking API change in pkg/git/repository; consumers must
migrate to signature.NewOpenPGPSigner or signature.NewSSHSigner. Since
pkg/git is still pre-1.0, the change lands in a coordinated minor.

Refs #398[1].

[1]: #398

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Calls gogit.Client.Commit with WithSigner(NewOpenPGPSigner(...)) and
verifies the resulting commit's gpgsig header through
signature.VerifyPGPSignature. The table-driven harness leaves room
for SSH and unsigned cases to land as new rows in follow-up commits.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Adds an ed25519 row to TestCommit_WithSigner that exercises
NewSSHSigner and verifies the SSHSIG-armored gpgsig header through
signature.VerifySSHSignature. Asserts the signature uses the "git"
namespace and round-trips against the corresponding authorized_keys
public key.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Adds an ecdsa-sha2-nistp256 row to TestCommit_WithSigner so the
non-ed25519 SSH-signing path is exercised explicitly.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Adds a nil-signer row to TestCommit_WithSigner that pins the nil-
interface guard at gogit/client.go: a typed-nil reaching go-git's
commit path would panic on signer.Sign. The row also documents the
contract that WithSigner(nil) produces an empty gpgsig header.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
Adds the supported-algorithms paragraph to the NewSSHSigner godoc so
the public surface signals up front which keys it will accept. No
behaviour change; validateSSHSigningKey already enforces this.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
NewSSHSigner now returns a typed sentinel error instead of a bare
errors.New for the encrypted-key-without-passphrase case, so
consumers can branch on it via errors.Is rather than matching the
literal error string.

Assisted-by: claude-code/claude-opus-4-7
Signed-off-by: Hidde Beydals <hidde@hhh.computer>
@hiddeco hiddeco force-pushed the git-ssh-signing branch from 7c001f1 to 7bce1f2 Compare June 5, 2026 15:10
@stefanprodan

Copy link
Copy Markdown
Member

The gitlab test is flaky, looks like a race condition at repo creation with GL API

@matheuscscp

Copy link
Copy Markdown
Member

Going to merge and release this before another small PR sneaks in

@matheuscscp matheuscscp merged commit 6400516 into main Jun 5, 2026
17 of 18 checks passed
@matheuscscp matheuscscp deleted the git-ssh-signing branch June 5, 2026 15:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/git Git and SSH related issues and pull requests area/security Security related issues and pull requests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants