Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5e7f264
adds SSH signature validation for git commits
bb-Ricardo Feb 26, 2026
a86a279
adds validation of key fingerprint string
bb-Ricardo Feb 27, 2026
5ee8487
adds better git signature detection format
bb-Ricardo Feb 27, 2026
9d568c7
adds detection for added signature prefixes
bb-Ricardo Feb 27, 2026
cb86156
adds tests for git.go
bb-Ricardo Feb 27, 2026
a382256
adds requested changes regarding naming
bb-Ricardo Feb 28, 2026
0aaa333
cleans up tests and removed duplicate test files
bb-Ricardo Mar 2, 2026
04b565e
fixes text fixtures public key names
bb-Ricardo Mar 10, 2026
107aaf0
adds 'SignatureTypeEmpty' check and improves description of 'GetSigna…
bb-Ricardo Mar 10, 2026
bfff3ec
Update git/signatures/testdata/gpg_signatures/generate_gpg_fixtures.sh
bb-Ricardo Mar 19, 2026
5b46d9c
removes insecure DSA keys from signature test
bb-Ricardo Mar 19, 2026
bcac539
updates git go.mod dependencies
bb-Ricardo Apr 27, 2026
21dcb59
fixes tests for unsigned tags
bb-Ricardo May 8, 2026
a67227a
renames package to signature and fixes requested changes
bb-Ricardo May 18, 2026
9d67599
switches signarture type from public to private
bb-Ricardo May 18, 2026
a9fc7ac
moves buildTag and buildCommitWithRef to internal/build package
bb-Ricardo May 18, 2026
5d4482b
improves signature validation to test all provided keys/keyrings if m…
bb-Ricardo May 19, 2026
a5ec92f
removes gnupg ed448 generated key test fixtures and adds mention to R…
bb-Ricardo May 19, 2026
1defd3d
removes verfied signers from ssh signature test fixtures
bb-Ricardo May 19, 2026
f080b82
updates test fixtures generation scripts and regenerated test fixtures
bb-Ricardo May 19, 2026
18724a3
fixes text fixtures creation description and adds test for unsigned c…
bb-Ricardo May 19, 2026
8ef0280
adds more test for malfromed signatures
bb-Ricardo May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@
# Dependency directories (remove the comment below to include it)
# vendor/

build/
/build/
bin/
testbin/
92 changes: 70 additions & 22 deletions git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ limitations under the License.
package git

import (
"bytes"
"errors"
"fmt"
"strings"
"time"

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/fluxcd/pkg/git/signature"
)

const (
Expand Down Expand Up @@ -113,19 +112,37 @@ func (c *Commit) AbsoluteReference() string {
return c.Hash.Digest()
}

// Deprecated: Verify is deprecated, use VerifySSH or VerifyGPG
// wrapper function to ensure backwards compatibility
func (c *Commit) Verify(keyRings ...string) (string, error) {
return c.VerifyGPG(keyRings...)
}

// Verify the Signature of the commit with the given key rings.
// It returns the fingerprint of the key the signature was verified
// with, or an error. It does not verify the signature of the referencing
// tag (if present). Users are expected to explicitly verify the referencing
// tag's signature using `c.ReferencingTag.Verify()`
func (c *Commit) Verify(keyRings ...string) (string, error) {
fingerprint, err := verifySignature(c.Signature, c.Encoded, keyRings...)
func (c *Commit) VerifyGPG(keyRings ...string) (string, error) {
fingerprint, err := signature.VerifyPGPSignature(c.Signature, c.Encoded, keyRings...)
if err != nil {
return "", fmt.Errorf("unable to verify Git commit: %w", err)
}
return fingerprint, nil
}

// VerifySSH verifies the SSH signature of the commit with the given authorized keys.
// It returns the fingerprint of the key the signature was verified with, or an error.
// It does not verify the signature of the referencing tag (if present). Users are
// expected to explicitly verify the referencing tag's signature using `c.ReferencingTag.VerifySSH()`
func (c *Commit) VerifySSH(authorizedKeys ...string) (string, error) {
Comment thread
bb-Ricardo marked this conversation as resolved.
fingerprint, err := signature.VerifySSHSignature(c.Signature, c.Encoded, authorizedKeys...)
if err != nil {
return "", fmt.Errorf("unable to verify Git commit SSH signature: %w", err)
}
return fingerprint, nil
}

// ShortMessage returns the first 50 characters of a commit subject.
func (c *Commit) ShortMessage() string {
subject := strings.Split(c.Message, "\n")[0]
Expand All @@ -152,17 +169,33 @@ type Tag struct {
Message string
}

// Deprecated: Verify is deprecated, use VerifySSH or VerifyGPG
// wrapper function to ensure backwards compatibility
func (t *Tag) Verify(keyRings ...string) (string, error) {
return t.VerifyGPG(keyRings...)
}

// Verify the Signature of the tag with the given key rings.
// It returns the fingerprint of the key the signature was verified
// with, or an error.
func (t *Tag) Verify(keyRings ...string) (string, error) {
fingerprint, err := verifySignature(t.Signature, t.Encoded, keyRings...)
func (t *Tag) VerifyGPG(keyRings ...string) (string, error) {
fingerprint, err := signature.VerifyPGPSignature(t.Signature, t.Encoded, keyRings...)
if err != nil {
return "", fmt.Errorf("unable to verify Git tag: %w", err)
}
return fingerprint, nil
}

// VerifySSH verifies the SSH signature of the tag with the given authorized keys.
// It returns the fingerprint of the key the signature was verified with, or an error.
func (t *Tag) VerifySSH(authorizedKeys ...string) (string, error) {
fingerprint, err := signature.VerifySSHSignature(t.Signature, t.Encoded, authorizedKeys...)
if err != nil {
return "", fmt.Errorf("unable to verify Git tag SSH signature: %w", err)
}
return fingerprint, nil
}

// String returns a short string representation of the tag in the format
// of <name@hash>, for eg: "1.0.0@a0c14dc8580a23f79bc654faa79c4f62b46c2c22"
// If the tag is lightweight, it won't have a hash, so it'll simply return
Expand Down Expand Up @@ -210,21 +243,36 @@ func IsSignedTag(t Tag) bool {
return t.Signature != ""
}

func verifySignature(sig string, payload []byte, keyRings ...string) (string, error) {
if sig == "" {
return "", fmt.Errorf("unable to verify payload as the provided signature is empty")
}
// IsPGPSigned returns true if the commit has a PGP signature.
func (c *Commit) IsPGPSigned() bool {
return signature.IsPGPSignature(c.Signature)
}

for _, r := range keyRings {
reader := strings.NewReader(r)
keyring, err := openpgp.ReadArmoredKeyRing(reader)
if err != nil {
return "", fmt.Errorf("unable to read armored key ring: %w", err)
}
signer, err := openpgp.CheckArmoredDetachedSignature(keyring, bytes.NewBuffer(payload), bytes.NewBufferString(sig), nil)
if err == nil {
return signer.PrimaryKey.KeyIdString(), nil
}
}
return "", fmt.Errorf("unable to verify payload with any of the given key rings")
// IsSSHSigned returns true if the commit has an SSH signature.
func (c *Commit) IsSSHSigned() bool {
return signature.IsSSHSignature(c.Signature)
}

// SignatureType returns the type of the commit signature as a string.
// It returns "openpgp" for PGP signatures, "ssh" for SSH signatures,
// and "unknown" for unrecognized or empty signatures.
func (c *Commit) SignatureType() string {
return signature.GetSignatureType(c.Signature)
}

// IsPGPSigned returns true if the tag has a PGP signature.
func (t *Tag) IsPGPSigned() bool {
return signature.IsPGPSignature(t.Signature)
}

// IsSSHSigned returns true if the tag has an SSH signature.
func (t *Tag) IsSSHSigned() bool {
return signature.IsSSHSignature(t.Signature)
}

// SignatureType returns the type of the tag signature as a string.
// It returns "openpgp" for PGP signatures, "ssh" for SSH signatures,
// and "unknown" for unrecognized or empty signatures.
func (t *Tag) SignatureType() string {
return signature.GetSignatureType(t.Signature)
}
Loading