Skip to content

object: introduce signature abstraction #705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git-fixtures/v4 v4.3.1
github.com/google/go-cmp v0.5.9
github.com/hiddeco/sshsig v0.1.0
github.com/imdario/mergo v0.3.13
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
github.com/jessevdk/go-flags v1.5.0
Expand All @@ -21,10 +22,10 @@ require (
github.com/sergi/go-diff v1.1.0
github.com/skeema/knownhosts v1.1.0
github.com/xanzy/ssh-agent v0.3.3
golang.org/x/crypto v0.6.0
golang.org/x/net v0.7.0
golang.org/x/sys v0.5.0
golang.org/x/text v0.7.0
golang.org/x/crypto v0.7.0
golang.org/x/net v0.8.0
golang.org/x/sys v0.6.0
golang.org/x/text v0.8.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/warnings.v0 v0.1.2 // indirect
)
32 changes: 23 additions & 9 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlK
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hiddeco/sshsig v0.1.0 h1:ehWA9PeBtDVAU7uULxUbQgw2e/JAB+ZKN29TIO33QUk=
github.com/hiddeco/sshsig v0.1.0/go.mod h1:PtIDi8GwgjGQDK0fUF1XhC24wjOymNbyiWd0NzXxTwo=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
Expand Down Expand Up @@ -59,10 +61,15 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
Expand All @@ -73,21 +80,23 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand All @@ -104,25 +113,29 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand All @@ -134,6 +147,7 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
22 changes: 9 additions & 13 deletions plumbing/object/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/ProtonMail/go-crypto/openpgp"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object/signature/pgp"
"github.com/go-git/go-git/v5/plumbing/storer"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/go-git/go-git/v5/utils/sync"
Expand Down Expand Up @@ -351,29 +352,24 @@ func (c *Commit) String() string {
)
}

// Signature returns the signature of the commit.
func (c *Commit) Signature() string {
return c.PGPSignature
}

// Verify performs PGP verification of the commit with a provided armored
// keyring and returns openpgp.Entity associated with verifying key on success.
func (c *Commit) Verify(armoredKeyRing string) (*openpgp.Entity, error) {
keyRingReader := strings.NewReader(armoredKeyRing)
keyring, err := openpgp.ReadArmoredKeyRing(keyRingReader)
verifier, err := pgp.NewVerifierFromArmoredKeyRing(keyRingReader)
if err != nil {
return nil, err
}

// Extract signature.
signature := strings.NewReader(c.PGPSignature)

encoded := &plumbing.MemoryObject{}
// Encode commit components, excluding signature and get a reader object.
if err := c.EncodeWithoutSignature(encoded); err != nil {
return nil, err
}
er, err := encoded.Reader()
entity, err := verifier.Verify(c)
if err != nil {
return nil, err
}

return openpgp.CheckArmoredDetachedSignature(keyring, er, signature, nil)
return entity.Concrete().(*openpgp.Entity), nil
}

func indent(t string) string {
Expand Down
19 changes: 19 additions & 0 deletions plumbing/object/signature/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package signature

// EntityType is the type of Entity. For example, PGP or SSH.
type EntityType string

// Entity is the entity which signed a VerifiableObject.
type Entity interface {
// Canonical returns the canonical identifier of the Entity. For example, a
// PGP key ID or an SSH public key fingerprint.
Canonical() string
// Type returns the type of the Entity. It can be useful to know if an
// Entity is of a certain type before attempting to cast Entity to an
// underlying type using Concrete.
Type() EntityType
// Concrete returns the underlying concrete type of the Entity.
// For example, a PGP entity or an SSH public key. This can be useful for
// extracting other details about the Entity than just Canonical.
Concrete() interface{}
}
34 changes: 34 additions & 0 deletions plumbing/object/signature/pgp/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package pgp

import (
"github.com/ProtonMail/go-crypto/openpgp"

"github.com/go-git/go-git/v5/plumbing/object/signature"
)

// EntityType is the PGP Entity type. It can be used to detect if a
// signature.Entity is of type PGP.
const EntityType signature.EntityType = "PGP"

// Entity is the PGP entity that signed a signature.VerifiableObject.
// Using the Entity method, you can get the underlying openpgp.Entity.
type Entity struct {
entity *openpgp.Entity
}

// Canonical returns the canonical identifier of the Entity. Which equals to
// the primary key ID of the openpgp.Entity.
func (s *Entity) Canonical() string {
return s.entity.PrimaryKey.KeyIdString()
}

// Type returns the EntityType of the Entity.
func (s *Entity) Type() signature.EntityType {
return EntityType
}

// Concrete returns the underlying concrete type of the Entity. In this case
// a pointer to an openpgp.Entity.
func (s *Entity) Concrete() interface{} {
return s.entity
}
45 changes: 45 additions & 0 deletions plumbing/object/signature/pgp/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package pgp

import (
"bytes"
"errors"

"github.com/ProtonMail/go-crypto/openpgp"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object/signature"
)

// Signer is a PGP signer. It can sign a signature.SignableObject object using
// an openpgp.Entity.
type Signer struct {
entity *openpgp.Entity
}

// NewSigner returns a new Signer using the given openpgp.Entity.
func NewSigner(entity *openpgp.Entity) (*Signer, error) {
if entity == nil {
return nil, errors.New("can not create a signer with a nil entity")
}
return &Signer{entity: entity}, nil
}

// Sign signs a signature.SignableObject object using the Signer's
// openpgp.Entity. It returns the signature of the object, or an error.
func (s *Signer) Sign(o signature.SignableObject) (string, error) {
encoded := &plumbing.MemoryObject{}
if err := o.Encode(encoded); err != nil {
return "", err
}

r, err := encoded.Reader()
if err != nil {
return "", err
}

var b bytes.Buffer
if err := openpgp.ArmoredDetachSign(&b, s.entity, r, nil); err != nil {
return "", err
}
return b.String(), nil
}
55 changes: 55 additions & 0 deletions plumbing/object/signature/pgp/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package pgp

import (
"io"
"strings"

"github.com/ProtonMail/go-crypto/openpgp"

"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object/signature"
)

// Verifier is a PGP verifier. It can verify a PGP signature using a list of
// entities.
type Verifier struct {
entities openpgp.EntityList
}

// NewVerifier creates a new PGP verifier from a list of entities.
func NewVerifier(entities openpgp.EntityList) *Verifier {
return &Verifier{entities: entities}
}

// NewVerifierFromArmoredKeyRing creates a new PGP verifier from an armored key
// ring. It returns an error if the key ring is not valid.
func NewVerifierFromArmoredKeyRing(r io.Reader) (*Verifier, error) {
entities, err := openpgp.ReadArmoredKeyRing(r)
if err != nil {
return nil, err
}
return NewVerifier(entities), nil
}

// Verify verifies a PGP signature using the verifier's entities. It returns the
// signature.Entity that signed the object, or an error.
func (v *Verifier) Verify(o signature.VerifiableObject) (signature.Entity, error) {
s := strings.NewReader(o.Signature())

encoded := &plumbing.MemoryObject{}
if err := o.EncodeWithoutSignature(encoded); err != nil {
return nil, err
}

er, err := encoded.Reader()
if err != nil {
return nil, err
}

entity, err := openpgp.CheckArmoredDetachedSignature(v.entities, er, s, nil)
if err != nil {
return nil, err
}

return &Entity{entity: entity}, nil
}
15 changes: 15 additions & 0 deletions plumbing/object/signature/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package signature

import "github.com/go-git/go-git/v5/plumbing"

// SignableObject is an object which can be signed.
type SignableObject interface {
Encode(o plumbing.EncodedObject) error
}

// ObjectSigner is capable of signing a SignableObject.
type ObjectSigner interface {
// Sign signs a SignableObject object. It returns the signature of the
// object.
Sign(o SignableObject) (string, error)
}
34 changes: 34 additions & 0 deletions plumbing/object/signature/ssh/entity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package ssh

import (
"golang.org/x/crypto/ssh"

"github.com/go-git/go-git/v5/plumbing/object/signature"
)

// EntityType is the SSH Entity type. It can be used to detect if a
// signature.Entity is of type SSH.
const EntityType signature.EntityType = "SSH"

// Entity is the SSH entity that signed a signature.VerifiableObject.
// Using the Entity method, you can get the underlying ssh.PublicKey.
type Entity struct {
publicKey ssh.PublicKey
}

// Canonical returns the canonical identifier of the Entity. Which equals to
// a marshalled authorized key (as in a known_hosts file).
func (s *Entity) Canonical() string {
return string(ssh.MarshalAuthorizedKey(s.publicKey))
}

// Type returns the EntityType of the Entity.
func (s *Entity) Type() signature.EntityType {
return EntityType
}

// Concrete returns the underlying concrete type of the Entity. In this case
// a pointer to an ssh.PublicKey.
func (s *Entity) Concrete() interface{} {
return s.publicKey
}
Loading