Skip to content

plumbing: object, add scheme-agnostic object verification API#2235

Open
pjbgf wants to merge 1 commit into
go-git:mainfrom
pjbgf:verifier
Open

plumbing: object, add scheme-agnostic object verification API#2235
pjbgf wants to merge 1 commit into
go-git:mainfrom
pjbgf:verifier

Conversation

@pjbgf

@pjbgf pjbgf commented Jun 26, 2026

Copy link
Copy Markdown
Member

Introduce a plugin-based, scheme-agnostic API for verifying Git object signatures, replacing the OpenPGP-only Commit.Verify/Tag.Verify.

  • Add plugin.Verifier, plugin.Verification and the plugin.ObjectVerifier() key (x/plugin), mirroring the existing Signer plugin.
  • Commit.Verify / Tag.Verify now take a context and VerifyOption(s), using the verifier from WithVerifier or, by default, the registered ObjectVerifier. Core ships only the contract; concrete verifiers live off-tree in go-git/x.
  • EncodeWithoutSignature returns a streaming io.Reader whose WriteTo feeds the verifier's hash directly, avoiding a full-payload buffer.
  • Signature and SignatureSHA256 are now []byte, removing string<->[]byte conversions in the sign/verify paths.
  • Multi-signature rejection moves to the OpenPGP verifier; core stays scheme-agnostic and no longer depends on ProtonMail/go-crypto.
  • Move the gpg/git conformance suite to go-git/x.

This results in:

  • By default, go-git has no default Verifier in-tree, which aligns with the approach taken for Signer.
  • The ObjectVerifier implementations will be placed in go-git/x so that users of go-git that don't need the third-party dependencies won't have them in their projects.

Global verifier

func init() {
    plugin.Register(plugin.ObjectVerifier(), func() plugin.Verifier {
        v, _ := gpg.FromKeyRing(myKeyRing)
        return v
    })
}

v, err := commit.Verify(ctx)

Explicit verifier

verifier, _ := gpg.FromKeyRing(myKeyRing)
v, err := tag.Verify(ctx, object.WithVerifier(verifier))

Proposed implementation for gpg and ssh can be seen here.

Supersedes #1883.
Relates to #1869.

Introduce a plugin-based, scheme-agnostic API for verifying Git object
signatures, replacing the OpenPGP-only Commit.Verify/Tag.Verify.

- Add plugin.Verifier, plugin.Verification and the plugin.ObjectVerifier()
  key (x/plugin), mirroring the existing Signer plugin.
- Commit.Verify / Tag.Verify now take a context and VerifyOption(s), using
  the verifier from WithVerifier or, by default, the registered
  ObjectVerifier. Core ships only the contract; concrete verifiers live
  off-tree in go-git/x.
- EncodeWithoutSignature returns a streaming io.Reader whose WriteTo feeds
  the verifier's hash directly, avoiding a full-payload buffer.
- Signature and SignatureSHA256 are now []byte, removing string<->[]byte
  conversions in the sign/verify paths.
- Multi-signature rejection moves to the OpenPGP verifier; core stays
  scheme-agnostic and no longer depends on ProtonMail/go-crypto.
- Move the gpg/git conformance suite to go-git/x.

Assisted-by: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Paulo Gomes <paulo@entire.io>
Copilot AI review requested due to automatic review settings June 26, 2026 13:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a scheme-agnostic, plugin-based Git object signature verification API (paralleling the existing signing plugin approach), while also refactoring commit/tag signature handling to use []byte and enabling streaming “encode-without-signature” payload generation for signing/verifying.

Changes:

  • Added plugin.Verifier / plugin.Verification and the plugin.ObjectVerifier() registry key to support out-of-tree verification implementations.
  • Updated Commit.Verify / Tag.Verify to take context.Context + VerifyOptions and delegate to a configured (or registered) Verifier.
  • Refactored signature storage to []byte and changed EncodeWithoutSignature to return a streaming io.Reader (via io.WriterTo) to avoid full-payload buffering.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
x/plugin/plugin_verifier.go Adds the verifier plugin contract and the ObjectVerifier plugin key.
worktree_commit.go Stores generated commit signatures as []byte.
worktree_commit_test.go Adjusts signature assertions for []byte commit signatures.
tests/objectverify/tag_test.go Removes in-tree tag verification conformance tests (moved off-tree).
tests/objectverify/main_test.go Removes in-tree object verification harness (moved off-tree).
tests/objectverify/commit_test.go Removes in-tree commit verification conformance tests (moved off-tree).
signer.go Switches signing to consume a streaming EncodeWithoutSignature() reader.
signer_test.go Updates example output for []byte signatures.
repository.go Updates tag signing path to return/store []byte signatures.
plumbing/object/verify.go Introduces core scheme-agnostic verify helper + options.
plumbing/object/verify_test.go Adds tests for verifier selection and registered default verifier behavior.
plumbing/object/verify_bench_test.go Adds benchmarks focusing on streaming verification behavior.
plumbing/object/tag.go Converts tag signatures to []byte, adds streaming EncodeWithoutSignature, and switches Verify to plugin-based verifier.
plumbing/object/tag_test.go Updates tag tests for []byte signatures and new EncodeWithoutSignature shape; removes OpenPGP Verify tests.
plumbing/object/tag_scanner.go Stores tag signature header data into []byte.
plumbing/object/signature.go Adds streaming signedReader, refactors signature parsing helpers, and makes signature stripping stream-oriented.
plumbing/object/signature_test.go Updates tests/fuzzing for the refactored signature parsing helpers.
plumbing/object/commit.go Converts commit signatures to []byte, adds streaming EncodeWithoutSignature, and switches Verify to plugin-based verifier.
plumbing/object/commit_test.go Updates commit tests for []byte signatures and new EncodeWithoutSignature shape; removes OpenPGP Verify tests.
plumbing/object/commit_scanner.go Stores commit signature header data into []byte.
go.sum Removes ProtonMail/go-crypto and related indirect sums.
go.mod Removes ProtonMail/go-crypto and related indirect dependencies.
EXTENDING.md Documents the new verification plugin API and usage patterns.

Comment thread plumbing/object/verify.go
Comment on lines +47 to +53
v := cfg.verifier
if v == nil {
var err error
if v, err = plugin.Get(plugin.ObjectVerifier()); err != nil {
return nil, err
}
}
Comment on lines +32 to +37
func (f *fakeVerifier) Verify(_ context.Context, message io.Reader, signature []byte) (*plugin.Verification, error) {
b, _ := io.ReadAll(message)
f.gotMessage = b
f.gotSignature = signature
return f.result, f.err
}
Method SignatureType
// Details carries scheme-specific data, for example an *openpgp.Entity
// for OpenPGP signatures. Callers type-assert on it based on Method.
Details any

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@Bluebugs This should enable any additional scheme-specific information to be shared with users (as per separate thread).

Trust levels becomes an implementation detail of the verifier, as opposed to the API first class citizen. I haven't added them to the initial GPG implementation, but that is something we should be able to easily add as a follow-up.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice. I haven't worked much on go-git lately as I don't want to overwhelm more the PR queue :-) But I might try to implement support for allowed_signers on top of your PR for go-git/x ( https://man7.org/linux/man-pages/man1/ssh-keygen.1.html#ALLOWED_SIGNERS ).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants