aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--options.go5
-rw-r--r--worktree_commit.go53
-rw-r--r--worktree_commit_test.go1
3 files changed, 48 insertions, 11 deletions
diff --git a/options.go b/options.go
index e748b91..ddd637b 100644
--- a/options.go
+++ b/options.go
@@ -1,6 +1,7 @@
package git
import (
+ "crypto"
"errors"
"fmt"
"regexp"
@@ -507,6 +508,10 @@ type CommitOptions struct {
// commit will not be signed. The private key must be present and already
// decrypted.
SignKey *openpgp.Entity
+ // Signer denotes a cryptographic signer to sign the commit with.
+ // A nil value here means the commit will not be signed.
+ // Takes precedence over SignKey.
+ Signer crypto.Signer
// Amend will create a new commit object and replace the commit that HEAD currently
// points to. Cannot be used with All nor Parents.
Amend bool
diff --git a/worktree_commit.go b/worktree_commit.go
index 4d811f3..18002f2 100644
--- a/worktree_commit.go
+++ b/worktree_commit.go
@@ -2,7 +2,10 @@ package git
import (
"bytes"
+ "crypto"
+ "crypto/rand"
"errors"
+ "io"
"path"
"sort"
"strings"
@@ -14,6 +17,7 @@ import (
"github.com/go-git/go-git/v5/storage"
"github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
"github.com/go-git/go-billy/v5"
)
@@ -125,12 +129,17 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb
ParentHashes: opts.Parents,
}
- if opts.SignKey != nil {
- sig, err := w.buildCommitSignature(commit, opts.SignKey)
+ // Convert SignKey into a Signer if set. Existing Signer should take priority.
+ signer := opts.Signer
+ if signer == nil && opts.SignKey != nil {
+ signer = &gpgSigner{key: opts.SignKey}
+ }
+ if signer != nil {
+ sig, err := w.buildCommitSignature(commit, signer)
if err != nil {
return plumbing.ZeroHash, err
}
- commit.PGPSignature = sig
+ commit.PGPSignature = string(sig)
}
obj := w.r.Storer.NewEncodedObject()
@@ -140,20 +149,44 @@ func (w *Worktree) buildCommitObject(msg string, opts *CommitOptions, tree plumb
return w.r.Storer.SetEncodedObject(obj)
}
-func (w *Worktree) buildCommitSignature(commit *object.Commit, signKey *openpgp.Entity) (string, error) {
+type gpgSigner struct {
+ key *openpgp.Entity
+}
+
+func (s *gpgSigner) Public() crypto.PublicKey {
+ return s.key.PrimaryKey
+}
+
+func (s *gpgSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
+ var cfg *packet.Config
+ if opts != nil {
+ cfg = &packet.Config{
+ DefaultHash: opts.HashFunc(),
+ }
+ }
+
+ var b bytes.Buffer
+ if err := openpgp.ArmoredDetachSign(&b, s.key, bytes.NewReader(digest), cfg); err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
+func (w *Worktree) buildCommitSignature(commit *object.Commit, signer crypto.Signer) ([]byte, error) {
encoded := &plumbing.MemoryObject{}
if err := commit.Encode(encoded); err != nil {
- return "", err
+ return nil, err
}
r, err := encoded.Reader()
if err != nil {
- return "", err
+ return nil, err
}
- var b bytes.Buffer
- if err := openpgp.ArmoredDetachSign(&b, signKey, r, nil); err != nil {
- return "", err
+ b, err := io.ReadAll(r)
+ if err != nil {
+ return nil, err
}
- return b.String(), nil
+
+ return signer.Sign(rand.Reader, b, nil)
}
// buildTreeHelper converts a given index.Index file into multiple git objects
diff --git a/worktree_commit_test.go b/worktree_commit_test.go
index 1ac1990..cc3c9a9 100644
--- a/worktree_commit_test.go
+++ b/worktree_commit_test.go
@@ -131,7 +131,6 @@ func (s *WorktreeSuite) TestCommitAmend(c *C) {
_, err = w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
c.Assert(err, IsNil)
-
amendedHash, err := w.Commit("bar\n", &CommitOptions{Amend: true})
c.Assert(err, IsNil)