Skip to content

Commit

Permalink
git: add support for linking a parent tag to a commit
Browse files Browse the repository at this point in the history
Add a new struct `AnnotatedTag` that represents a Git annotated tag and
add it as a new field `Commit.Parent`, allowing for a commit to contain
information about its parent tag. Add support for verifying the parent
tag as well.

Signed-off-by: Sanskar Jaiswal <[email protected]>
  • Loading branch information
aryan9600 committed Jun 30, 2023
1 parent c4f37dc commit 648b9f9
Showing 1 changed file with 51 additions and 16 deletions.
67 changes: 51 additions & 16 deletions git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type Commit struct {
Encoded []byte
// Message is the commit message, containing arbitrary text.
Message string
// Parent is the parent tag, that points to this commit.
Parent *AnnotatedTag
}

// String returns a string representation of the Commit, composed
Expand Down Expand Up @@ -115,22 +117,7 @@ func (c *Commit) AbsoluteReference() string {
// It returns the fingerprint of the key the signature was verified
// with, or an error.
func (c *Commit) Verify(keyRing ...string) (string, error) {
if c.Signature == "" {
return "", fmt.Errorf("commit does not have a PGP signature")
}

for _, r := range keyRing {
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(c.Encoded), bytes.NewBufferString(c.Signature), nil)
if err == nil {
return signer.PrimaryKey.KeyIdString(), nil
}
}
return "", fmt.Errorf("unable to verify commit with any of the given key rings")
return verifySignature(keyRing, c.Signature, c.Encoded)
}

// ShortMessage returns the first 50 characters of a commit subject.
Expand All @@ -143,6 +130,35 @@ func (c *Commit) ShortMessage() string {
return subject
}

// AnnotatedTag represents a Git annotated tag.
type AnnotatedTag struct {
// Hash is the hash of the tag.
Hash Hash
// Name is the name of the tag.
Name string
// Author is the original author of the tag.
Author Signature
// Signature is the PGP signature of the tag.
Signature string
// Encoded is the encoded tag, without any signature.
Encoded []byte
// Message is the tagmessage, containing arbitrary text.
Message string
}

// 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 *AnnotatedTag) Verify(keyRing ...string) (string, error) {
return verifySignature(keyRing, t.Signature, t.Encoded)
}

// String returns a short string representation of the tag in the format
// of <name@hash>, for eg: <1.0.0@a0c14dc8580a23f79bc654faa79c4f62b46c2c22>
func (t *AnnotatedTag) String() string {
return fmt.Sprintf("%s@%s", t.Name, t.Hash.String())
}

// ErrRepositoryNotFound indicates that the repository (or the ref in
// question) does not exist at the given URL.
type ErrRepositoryNotFound struct {
Expand All @@ -168,3 +184,22 @@ func IsConcreteCommit(c Commit) bool {
}
return false
}

func verifySignature(keyRing ...string, sig string, payload []byte) (string, error) {
if sig == "" {
return "", fmt.Errorf("commit does not have a PGP signature")
}

for _, r := range keyRing {
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 commit with any of the given key rings")
}

0 comments on commit 648b9f9

Please sign in to comment.