aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOri Rawlings <orirawlings@gmail.com>2017-11-24 17:38:51 -0500
committerGitHub <noreply@github.com>2017-11-24 17:38:51 -0500
commitda62c676588f73cc6d1f05f621fead6890c65eea (patch)
treedff2708f4c2b9fcbb22e73a7fa14ca6a8041b8fc
parentd92d437af0fb23865c79e45b4acd3d7544593260 (diff)
parente1ce83fcaf620db6a03026185617908a83dfe841 (diff)
downloadgo-git-da62c676588f73cc6d1f05f621fead6890c65eea.tar.gz
Merge pull request #658 from darkowlzz/tag_sign_and_verify
plumbing: object/tag, add signature and verification support
-rw-r--r--plumbing/object/commit.go13
-rw-r--r--plumbing/object/tag.go75
-rw-r--r--plumbing/object/tag_test.go91
3 files changed, 172 insertions, 7 deletions
diff --git a/plumbing/object/commit.go b/plumbing/object/commit.go
index e54eb7d..a317714 100644
--- a/plumbing/object/commit.go
+++ b/plumbing/object/commit.go
@@ -223,6 +223,10 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
// Encode transforms a Commit into a plumbing.EncodedObject.
func (b *Commit) Encode(o plumbing.EncodedObject) error {
+ return b.encode(o, true)
+}
+
+func (b *Commit) encode(o plumbing.EncodedObject, includeSig bool) error {
o.SetType(plumbing.CommitObject)
w, err := o.Writer()
if err != nil {
@@ -257,7 +261,7 @@ func (b *Commit) Encode(o plumbing.EncodedObject) error {
return err
}
- if b.PGPSignature != "" {
+ if b.PGPSignature != "" && includeSig {
if _, err = fmt.Fprint(w, "pgpsig"); err != nil {
return err
}
@@ -325,12 +329,9 @@ func (c *Commit) Verify(armoredKeyRing string) (*openpgp.Entity, error) {
// Extract signature.
signature := strings.NewReader(c.PGPSignature)
- // Remove signature. Keep only the commit components.
- c.PGPSignature = ""
-
- // Encode commit and get a reader object.
encoded := &plumbing.MemoryObject{}
- if err := c.Encode(encoded); err != nil {
+ // Encode commit components, excluding signature and get a reader object.
+ if err := c.encode(encoded, false); err != nil {
return nil, err
}
er, err := encoded.Reader()
diff --git a/plumbing/object/tag.go b/plumbing/object/tag.go
index d8b71a0..19e55cf 100644
--- a/plumbing/object/tag.go
+++ b/plumbing/object/tag.go
@@ -6,6 +6,9 @@ import (
"fmt"
"io"
stdioutil "io/ioutil"
+ "strings"
+
+ "golang.org/x/crypto/openpgp"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/storer"
@@ -30,6 +33,8 @@ type Tag struct {
Tagger Signature
// Message is an arbitrary text message.
Message string
+ // PGPSignature is the PGP signature of the tag.
+ PGPSignature string
// TargetType is the object type of the target.
TargetType plumbing.ObjectType
// Target is the hash of the target object.
@@ -124,13 +129,46 @@ func (t *Tag) Decode(o plumbing.EncodedObject) (err error) {
if err != nil {
return err
}
- t.Message = string(data)
+
+ var pgpsig bool
+ // Check if data contains PGP signature.
+ if bytes.Contains(data, []byte(beginpgp)) {
+ // Split the lines at newline.
+ messageAndSig := bytes.Split(data, []byte("\n"))
+
+ for _, l := range messageAndSig {
+ if pgpsig {
+ if bytes.Contains(l, []byte(endpgp)) {
+ t.PGPSignature += endpgp + "\n"
+ pgpsig = false
+ } else {
+ t.PGPSignature += string(l) + "\n"
+ }
+ continue
+ }
+
+ // Check if it's the beginning of a PGP signature.
+ if bytes.Contains(l, []byte(beginpgp)) {
+ t.PGPSignature += beginpgp + "\n"
+ pgpsig = true
+ continue
+ }
+
+ t.Message += string(l) + "\n"
+ }
+ } else {
+ t.Message = string(data)
+ }
return nil
}
// Encode transforms a Tag into a plumbing.EncodedObject.
func (t *Tag) Encode(o plumbing.EncodedObject) error {
+ return t.encode(o, true)
+}
+
+func (t *Tag) encode(o plumbing.EncodedObject, includeSig bool) error {
o.SetType(plumbing.TagObject)
w, err := o.Writer()
if err != nil {
@@ -156,6 +194,16 @@ func (t *Tag) Encode(o plumbing.EncodedObject) error {
return err
}
+ if t.PGPSignature != "" && includeSig {
+ // Split all the signature lines and write with a newline at the end.
+ lines := strings.Split(t.PGPSignature, "\n")
+ for _, line := range lines {
+ if _, err = fmt.Fprintf(w, "%s\n", line); err != nil {
+ return err
+ }
+ }
+ }
+
return err
}
@@ -225,6 +273,31 @@ func (t *Tag) String() string {
)
}
+// Verify performs PGP verification of the tag with a provided armored
+// keyring and returns openpgp.Entity associated with verifying key on success.
+func (t *Tag) Verify(armoredKeyRing string) (*openpgp.Entity, error) {
+ keyRingReader := strings.NewReader(armoredKeyRing)
+ keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader)
+ if err != nil {
+ return nil, err
+ }
+
+ // Extract signature.
+ signature := strings.NewReader(t.PGPSignature)
+
+ encoded := &plumbing.MemoryObject{}
+ // Encode tag components, excluding signature and get a reader object.
+ if err := t.encode(encoded, false); err != nil {
+ return nil, err
+ }
+ er, err := encoded.Reader()
+ if err != nil {
+ return nil, err
+ }
+
+ return openpgp.CheckArmoredDetachedSignature(keyring, er, signature)
+}
+
// TagIter provides an iterator for a set of tags.
type TagIter struct {
storer.EncodedObjectIter
diff --git a/plumbing/object/tag_test.go b/plumbing/object/tag_test.go
index 608fe72..9900093 100644
--- a/plumbing/object/tag_test.go
+++ b/plumbing/object/tag_test.go
@@ -285,3 +285,94 @@ func (s *TagSuite) TestLongTagNameSerialization(c *C) {
c.Assert(err, IsNil)
c.Assert(decoded.Name, Equals, longName)
}
+
+func (s *TagSuite) TestPGPSignatureSerialization(c *C) {
+ encoded := &plumbing.MemoryObject{}
+ decoded := &Tag{}
+ tag := s.tag(c, plumbing.NewHash("b742a2a9fa0afcfa9a6fad080980fbc26b007c69"))
+
+ pgpsignature := `-----BEGIN PGP SIGNATURE-----
+
+iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
+LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
+hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
+ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
+8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
+RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
+=EFTF
+-----END PGP SIGNATURE-----
+`
+ tag.PGPSignature = pgpsignature
+
+ err := tag.Encode(encoded)
+ c.Assert(err, IsNil)
+
+ err = decoded.Decode(encoded)
+ c.Assert(err, IsNil)
+ c.Assert(decoded.PGPSignature, Equals, pgpsignature)
+}
+
+func (s *TagSuite) TestVerify(c *C) {
+ ts := time.Unix(1511524851, 0)
+ loc, _ := time.LoadLocation("Asia/Kolkata")
+ tag := &Tag{
+ Name: "v0.2",
+ Tagger: Signature{Name: "Sunny", Email: "me@darkowlzz.space", When: ts.In(loc)},
+ Message: `This is a signed tag
+`,
+ TargetType: plumbing.CommitObject,
+ Target: plumbing.NewHash("064f92fe00e70e6b64cb358a65039daa4b6ae8d2"),
+ PGPSignature: `
+-----BEGIN PGP SIGNATURE-----
+
+iQFHBAABCAAxFiEEoRt6IzxHaZkkUslhQyLeMqcmyU4FAloYCg8THG1lQGRhcmtv
+d2x6ei5zcGFjZQAKCRBDIt4ypybJTs0cCACjQZe2610t3gfbUPbgQiWDL9uvlCeb
+sNSeTC6hLAFSvHTMqLr/6RpiLlfQXyATD7TZUH0DUSLsERLheG82OgVxkOTzPCpy
+GL6iGKeZ4eZ1KiV+SBPjqizC9ShhGooPUw9oUSVdj4jsaHDdDHtY63Pjl0KvJmms
+OVi9SSxjeMbmaC81C8r0ZuOLTXJh/JRKh2BsehdcnK3736BK+16YRD7ugXLpkQ5d
+nsCFVbuYYoLMoJL5NmEun0pbUrpY+MI8VPK0f9HV5NeaC4NksC+ke/xYMT+P2lRL
+CN+9zcCIU+mXr2fCl1xOQcnQzwOElObDxpDcPcxVn0X+AhmPc+uj0mqD
+=l75D
+-----END PGP SIGNATURE-----
+`,
+ }
+
+ armoredKeyRing := `
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFmtHgABCADnfThM7q8D4pgUub9jMppSpgFh3ev84g3Csc3yQUlszEOVgXmu
+YiSWP1oAiWFQ8ahCydh3LT8TnEB2QvoRNiExUI5XlXFwVfKW3cpDu8gdhtufs90Q
+NvpaHOgTqRf/texGEKwXi6fvS47fpyaQ9BKNdN52LeaaHzDDZkVsAFmroE+7MMvj
+P4Mq8qDn2WcWnX9zheQKYrX6Cs48Tx80eehHor4f/XnuaP8DLmPQx7URdJ0Igckh
+N+i91Qv2ujin8zxUwhkfus66EZS9lQ4qR9iVHs4WHOs3j7whsejd4VhajonilVHj
+uqTtqHmpN/4njbIKb8q8uQkS26VQYoSYm2UvABEBAAG0GlN1bm55IDxtZUBkYXJr
+b3dsenouc3BhY2U+iQFUBBMBCAA+FiEEoRt6IzxHaZkkUslhQyLeMqcmyU4FAlmt
+HgACGwMFCQPCZwAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQQyLeMqcmyU7V
+nAf+J5BYu26B2i+iwctOzDRFcPwCLka9cBwe5wcDvoF2qL8QRo8NPWBBH4zWHa/k
+BthtGo1b89a53I2hnTwTQ0NOtAUNV+Vvu6nOHJd9Segsx3E1nM43bd2bUfGJ1eeO
+jDOlOvtP4ozuV6Ej+0Ln2ouMOc87yAwbAzTfQ9axU6CKUbqy0/t2dW1jdKntGH+t
+VPeFxJHL2gXjP89skCSPYA7yKqqyJRPFvC+7rde1OLdCmZi4VwghUiNbh3s1+xM3
+gfr2ahsRDTN2SQzwuHu4y1EgZgPtuWfRxzHqduoRoSgfOfFr9H9Il3UMHf2Etleu
+rif40YZJhge6STwsIycGh4wOiLkBDQRZrR4AAQgArpUvPdGC/W9X4AuZXrXEShvx
+TqM4K2Jk9n0j+ABx87k9fm48qgtae7+TayMbb0i7kcbgnjltKbauTbyRbju/EJvN
+CdIw76IPpjy6jUM37wG2QGLFo6Ku3x8/ZpNGGOZ8KMU258/EBqDlJQ/4g4kJ8D+m
+9yOH0r6/Xpe/jOY2V8Jo9pdFTm+8eAsSyZF0Cl7drz603Pymq1IS2wrwQbdxQA/w
+B75pQ5es7X34Ac7/9UZCwCPmZDAldnjHyw5dZgZe8XLrG84BIfbG0Hj8PjrFdF1D
+Czt9bk+PbYAnLORW2oX1oedxVrNFo5UrbWgBSjA1ppbGFjwSDHFlyjuEuxqyFwAR
+AQABiQE8BBgBCAAmFiEEoRt6IzxHaZkkUslhQyLeMqcmyU4FAlmtHgACGwwFCQPC
+ZwAACgkQQyLeMqcmyU7ZBggArzc8UUVSjde987Vqnu/S5Cv8Qhz+UB7gAFyTW2iF
+VYvB86r30H/NnfjvjCVkBE6FHCNHoxWVyDWmuxKviB7nkReHuwqniQHPgdJDcTKC
+tBboeX2IYBLJbEvEJuz5NSvnvFuYkIpZHqySFaqdl/qu9XcmoPL5AmIzIFOeiNty
+qT0ldkf3ru6yQQDDqBDpkfz4AzkpFnLYL59z6IbJDK2Hz7aKeSEeVOGiZLCjIZZV
+uISZThYqh5zUkvF346OHLDqfDdgQ4RZriqd/DTtRJPlz2uL0QcEIjJuYCkG0UWgl
+sYyf9RfOnw/KUFAQbdtvLx3ikODQC+D3KBtuKI9ISHQfgw==
+=FPev
+-----END PGP PUBLIC KEY BLOCK-----
+`
+
+ e, err := tag.Verify(armoredKeyRing)
+ c.Assert(err, IsNil)
+
+ _, ok := e.Identities["Sunny <me@darkowlzz.space>"]
+ c.Assert(ok, Equals, true)
+}