diff options
author | Billy Lynch <billy@chainguard.dev> | 2024-01-11 14:42:52 -0500 |
---|---|---|
committer | Billy Lynch <billy@chainguard.dev> | 2024-01-18 12:56:55 -0500 |
commit | e1bb0e2cb68265458d4540ae566c255263d918a2 (patch) | |
tree | 139795e0c80abefba237d422952d7cfa9e84deef /worktree_commit.go | |
parent | a6e934f1f76996c7f92cb4fde708170c1eb5d032 (diff) | |
download | go-git-e1bb0e2cb68265458d4540ae566c255263d918a2.tar.gz |
git: worktree_commit, Add crypto.Signer option to CommitOptions.
This change adds a new crypto.Signer option to CommitOptions as an
alternative to SignKey to allow alternative commit signers to be used.
This change byitself does not add other signing methods (e.g. ssh,
x509, gitsign), but gives callers the ability to add their own.
This roughly follows git's sign_buffer approach where go-git handles the
commit message body encoding, and hands off the encoded []byte to the signing
implementation for the signature to be returned.
Signed-off-by: Billy Lynch <billy@chainguard.dev>
Diffstat (limited to 'worktree_commit.go')
-rw-r--r-- | worktree_commit.go | 53 |
1 files changed, 43 insertions, 10 deletions
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 |