Skip to content

Commit

Permalink
WIP: go-git based signer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wlynch committed Feb 20, 2024
1 parent 57153a0 commit c9efc2f
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 15 deletions.
95 changes: 95 additions & 0 deletions e2e/sign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//go:build e2e
// +build e2e

package e2e

import (
"context"
"crypto/x509"
"testing"
"time"

"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/sigstore/gitsign/internal/fulcio/fulcioroots"
"github.com/sigstore/gitsign/internal/git/gittest"
"github.com/sigstore/gitsign/pkg/fulcio"
gsgit "github.com/sigstore/gitsign/pkg/git"
"github.com/sigstore/gitsign/pkg/gitsign"
"github.com/sigstore/gitsign/pkg/rekor"
"github.com/sigstore/sigstore/pkg/oauth"
"github.com/sigstore/sigstore/pkg/oauthflow"
"github.com/sigstore/sigstore/pkg/tuf"
)

func TestSign(t *testing.T) {
ctx := context.Background()

flow := &oauthflow.InteractiveIDTokenGetter{
HTMLPage: oauth.InteractiveSuccessHTML,
}
fulcio, err := fulcio.NewClient("https://fulcio.sigstore.dev", fulcio.OIDCOptions{
ClientID: "sigstore",
Issuer: "https://oauth2.sigstore.dev/auth",
TokenGetter: flow,
})
if err != nil {
t.Fatal(err)
}
rekor, err := rekor.NewWithOptions(ctx, "https://rekor.sigstore.dev")
if err != nil {
t.Fatal(err)
}
signer, err := gitsign.NewSigner(ctx, fulcio, rekor)
if err != nil {
t.Fatal(err)
}

// Make a commit + sign it
storage := memory.NewStorage()
repo, err := git.Init(storage, memfs.New())
if err != nil {
panic(err)
}
w, err := repo.Worktree()
if err != nil {
panic(err)
}
sha, err := w.Commit("example commit", &git.CommitOptions{
Author: &object.Signature{
Name: "John Doe",
Email: "[email protected]",
When: time.UnixMicro(1234567890).UTC(),
},
Signer: signer,
AllowEmptyCommits: true,
})
if err != nil {
t.Fatal(err)
}
commit, err := repo.CommitObject(sha)
if err != nil {
t.Fatal(err)
}
body := gittest.MarshalCommitBody(t, commit)
sig := []byte(commit.PGPSignature)

// Verify the commit
// TODO: export verifier functions?
tuf.Initialize(ctx, tuf.DefaultRemoteRoot, nil)
root, intermediate, err := fulcioroots.New(x509.NewCertPool(), fulcioroots.FromTUF(ctx))
if err != nil {
t.Fatalf("error getting certificate root: %v", err)
}
verifier, err := gsgit.NewCertVerifier(gsgit.WithRootPool(root), gsgit.WithIntermediatePool(intermediate))
if err != nil {
t.Fatal(err)
}
summary, err := gsgit.Verify(ctx, verifier, rekor, body, sig, true)
if err != nil {
t.Fatal(err)
}
t.Log(summary.LogEntry.LogID)
}
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/jonboulle/clockwork v0.4.0
github.com/mattn/go-tty v0.0.5
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e
github.com/secure-systems-lab/go-securesystemslib v0.8.0
github.com/sigstore/cosign/v2 v2.2.3
github.com/sigstore/fulcio v1.4.3
Expand Down Expand Up @@ -48,7 +49,7 @@ require (
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect
Expand Down Expand Up @@ -168,7 +169,7 @@ require (
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/timestamp-authority v1.2.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
Expand Down Expand Up @@ -233,3 +234,5 @@ require (
)

retract v0.7.0

replace github.com/go-git/go-git/v5 => ../../go-git/go-git
13 changes: 7 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E=
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
Expand Down Expand Up @@ -253,8 +253,6 @@ github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down Expand Up @@ -338,6 +336,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
Expand Down Expand Up @@ -534,6 +533,8 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e h1:51xcRlSMBU5rhM9KahnJGfEsBPVPz3182TgFRowA8yY=
github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
Expand All @@ -552,8 +553,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbm
github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sigstore/cosign/v2 v2.2.3 h1:WX7yawI+EXu9h7S5bZsfYCbB9XW6Jc43ctKy/NoOSiA=
Expand Down
5 changes: 3 additions & 2 deletions internal/fulcio/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/sigstore/gitsign/internal/config"
"github.com/sigstore/gitsign/internal/fulcio/fulcioroots"
"github.com/sigstore/gitsign/internal/signerverifier"
"github.com/sigstore/gitsign/pkg/fulcio"
"github.com/sigstore/sigstore/pkg/oauth"
"github.com/sigstore/sigstore/pkg/oauthflow"
"github.com/sigstore/sigstore/pkg/signature"
Expand Down Expand Up @@ -233,8 +234,8 @@ func (f *IdentityFactory) NewIdentity(ctx context.Context, cfg *config.Config) (
return nil, fmt.Errorf("generating private key: %w", err)
}

client, err := NewClient(cfg.Fulcio,
OIDCOptions{
client, err := fulcio.NewClient(cfg.Fulcio,
fulcio.OIDCOptions{
Issuer: cfg.Issuer,
ClientID: clientID,
RedirectURL: cfg.RedirectURL,
Expand Down
12 changes: 8 additions & 4 deletions internal/fulcio/fulcio.go → pkg/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ import (
"github.com/sigstore/sigstore/pkg/oauthflow"
)

type Client interface {
GetCert(crypto.Signer) (*api.CertificateResponse, error)
}

// Client provides a fulcio client with helpful options for configuring OIDC
// flows.
type Client struct {
type ClientImpl struct {
api.LegacyClient
oidc OIDCOptions
}
Expand All @@ -43,20 +47,20 @@ type OIDCOptions struct {
TokenGetter oauthflow.TokenGetter
}

func NewClient(fulcioURL string, opts OIDCOptions) (*Client, error) {
func NewClient(fulcioURL string, opts OIDCOptions) (*ClientImpl, error) {
u, err := url.Parse(fulcioURL)
if err != nil {
return nil, err
}
client := api.NewClient(u, api.WithUserAgent("gitsign"))
return &Client{
return &ClientImpl{
LegacyClient: client,
oidc: opts,
}, nil
}

// GetCert exchanges the given private key for a Fulcio certificate.
func (c *Client) GetCert(priv crypto.Signer) (*api.CertificateResponse, error) {
func (c *ClientImpl) GetCert(priv crypto.Signer) (*api.CertificateResponse, error) {
pubBytes, err := x509.MarshalPKIXPublicKey(priv.Public())
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func TestGetCert(t *testing.T) {
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
email := "[email protected]"

client := &Client{
client := &ClientImpl{
// fakeFulcio is what will be doing the validation.
LegacyClient: &fakeFulcio{
signer: key,
Expand Down
73 changes: 73 additions & 0 deletions pkg/gitsign/signer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Package gitsign provides a signer for signing git commits and tags via Gitsign keyless flow.
package gitsign

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"io"

fulciointernal "github.com/sigstore/gitsign/internal/fulcio"
"github.com/sigstore/gitsign/internal/signature"
"github.com/sigstore/gitsign/pkg/fulcio"
"github.com/sigstore/gitsign/pkg/rekor"
)

type PrivateKeySigner interface {
crypto.PrivateKey
crypto.Signer
}

type fulcioSigner struct {
ctx context.Context
key PrivateKeySigner
fulcio fulcio.Client
rekor rekor.Writer
}

func NewSigner(ctx context.Context, fulcio fulcio.Client, rekor rekor.Writer) (*fulcioSigner, error) {

Check warning on line 31 in pkg/gitsign/signer.go

View workflow job for this annotation

GitHub Actions / lint

unexported-return: exported func NewSigner returns unexported type *gitsign.fulcioSigner, which can be annoying to use (revive)
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("generating private key: %w", err)
}
return &fulcioSigner{
ctx: ctx,
key: priv,
fulcio: fulcio,
rekor: rekor,
}, nil
}

func (f *fulcioSigner) Sign(message io.Reader) ([]byte, error) {
cert, err := f.fulcio.GetCert(f.key)
if err != nil {
return nil, fmt.Errorf("error getting fulcio cert: %w", err)
}

id := &fulciointernal.Identity{
PrivateKey: f.key,
CertPEM: cert.CertPEM,
ChainPEM: cert.ChainPEM,
}

body, err := io.ReadAll(message)
if err != nil {
return nil, fmt.Errorf("error reading message: %w", err)
}

resp, err := signature.Sign(f.ctx, id, body, signature.SignOptions{
Rekor: f.rekor,

// TODO: make SignOptions configurable?
Armor: true,
Detached: true,
IncludeCerts: -2,
})
if err != nil {
return nil, err
}
return resp.Signature, nil
}
1 change: 1 addition & 0 deletions pkg/gitsign/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package gitsign

0 comments on commit c9efc2f

Please sign in to comment.