Skip to content

Commit

Permalink
Modernize attest statements
Browse files Browse the repository at this point in the history
attest no longer uses the cosign library to generate the Statement
used in the attestation.

This lets us:

1. Use `gitCommit` as the digest type (which is what we were doing anyways)
2. Use the correct `_type` value for modern in-toto statements

In the future it will let us add annotations on the subject.

Note that this also changes the user behavior in that it:
1. Changes the things mentioned above.
2. Doesn't let users use the convenience attestation types from the command
   line.  They must instead specify the full predicate type.

refs #611

Signed-off-by: Tom Hennen <[email protected]>
  • Loading branch information
TomHennen committed Jan 19, 2025
1 parent 0c36055 commit fad643d
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/cli/gitsign_attest.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ gitsign attest [flags]
-f, --filepath string attestation filepath
-h, --help help for attest
--objtype string [commit | tree] - Git object type to attest (default "commit")
--type string specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom")
--type string specify a predicate type URI
```

### SEE ALSO
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/go-openapi/strfmt v0.23.0
github.com/go-openapi/swag v0.23.0
github.com/google/go-cmp v0.6.0
github.com/in-toto/attestation v1.1.0
github.com/in-toto/in-toto-golang v0.9.0
github.com/jonboulle/clockwork v0.5.0
github.com/mattn/go-tty v0.0.7
Expand Down
39 changes: 28 additions & 11 deletions internal/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,18 @@ import (
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/storage"
"github.com/go-openapi/strfmt"
spb "github.com/in-toto/attestation/go/v1"
"github.com/jonboulle/clockwork"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
"github.com/sigstore/cosign/v2/pkg/cosign/attestation"
"github.com/sigstore/cosign/v2/pkg/types"
utils "github.com/sigstore/gitsign/internal"
gitsignconfig "github.com/sigstore/gitsign/internal/config"
rekorclient "github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/rekor/pkg/generated/models"
dssesig "github.com/sigstore/sigstore/pkg/signature/dsse"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/structpb"
)

const (
Expand Down Expand Up @@ -111,7 +113,7 @@ func NewNamedReader(r io.Reader, name string) Reader {
// refName: What ref to write to (e.g. refs/attestations/commits)
// sha: Commit SHA you are attesting to.
// input: Attestation file input.
// attType: Attestation type. See [attestation.GenerateStatement] for allowed values.
// attType: Attestation (predicate) type URI corresponding to the input (predicate).
func (a *Attestor) WriteAttestation(ctx context.Context, refName string, sha plumbing.Hash, input Reader, attType string) (plumbing.Hash, error) {
b, err := io.ReadAll(input)
if err != nil {
Expand Down Expand Up @@ -226,18 +228,32 @@ func encode(store storage.Storer, enc Encoder) (plumbing.Hash, error) {
return store.SetEncodedObject(obj)
}

func generateStatement(pred []byte, attType string, sha plumbing.Hash) (*spb.Statement, error) {
sub := []*spb.ResourceDescriptor{{
Digest: map[string]string{"gitCommit": sha.String()},
}}

var predPb structpb.Struct
err := json.Unmarshal(pred, &predPb)
if err != nil {
return nil, err
}

return &spb.Statement{
Type: spb.StatementTypeUri,
Subject: sub,
PredicateType: attType,
Predicate: &predPb,
}, nil
}

func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, attType string) ([]byte, error) {
// Generate attestation
sh, err := attestation.GenerateStatement(attestation.GenerateOpts{
Predicate: bytes.NewBuffer(b),
Type: attType,
Digest: sha.String(),
Time: clock.Now,
})
sh, err := generateStatement(b, attType, sha)

if err != nil {
return nil, err
}
payload, err := json.Marshal(sh)
payload, err := protojson.Marshal(sh)
if err != nil {
return nil, err
}
Expand All @@ -248,7 +264,8 @@ func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte,
}

rekorHost, rekorBasePath := utils.StripURL(a.config.Rekor)
tc := &rekorclient.TransportConfig{

tc := &rekorclient.TransportConfig{
Host: rekorHost,
BasePath: rekorBasePath,
Schemes: []string{"https"},
Expand Down
14 changes: 7 additions & 7 deletions internal/attest/attest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestAttestCommitRef(t *testing.T) {
},
}
t.Run("base", func(t *testing.T) {
attest1, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest1, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand All @@ -99,7 +99,7 @@ func TestAttestCommitRef(t *testing.T) {

t.Run("noop", func(t *testing.T) {
// Write same attestation to the same commit - should be a no-op.
attest2, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest2, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand All @@ -117,7 +117,7 @@ func TestAttestCommitRef(t *testing.T) {
t.Fatal(err)
}

attest3, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest3, err := attestor.WriteAttestation(ctx, CommitRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand Down Expand Up @@ -170,7 +170,7 @@ func TestAttestTreeRef(t *testing.T) {
},
}
t.Run("base", func(t *testing.T) {
attest1, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest1, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand All @@ -179,7 +179,7 @@ func TestAttestTreeRef(t *testing.T) {

t.Run("noop", func(t *testing.T) {
// Write same attestation to the same commit - should be a no-op.
attest2, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest2, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand All @@ -197,7 +197,7 @@ func TestAttestTreeRef(t *testing.T) {
}
sha = resolveTree(t, repo, sha)

attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand All @@ -208,7 +208,7 @@ func TestAttestTreeRef(t *testing.T) {
// Make a new commit, write new attestation.
sha = resolveTree(t, repo, writeRepo(t, w, fs, "testdata/bar.txt"))

attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom")
attest3, err := attestor.WriteAttestation(ctx, TreeRef, sha, NewNamedReader(bytes.NewBufferString(content), name), "custom-pred-type")
if err != nil {
t.Fatalf("WriteAttestation: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/attest/testdata/test.json.provenance
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://cosign.sigstore.dev/attestation/v1","subject":[{"name":"","digest":{"sha256":"{{.}}"}}],"predicate":{"Data":"{\"foo\":\"bar\"}","Timestamp":"1984-04-04T00:00:00Z"}}
{"_type":"https://in-toto.io/Statement/v1","subject":[{"digest":{"gitCommit":"{{.}}"}}],"predicateType":"custom-pred-type","predicate":{"foo":"bar"}}
2 changes: 1 addition & 1 deletion internal/commands/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type options struct {
func (o *options) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.FlagObjectType, "objtype", FlagObjectTypeCommit, "[commit | tree] - Git object type to attest")
cmd.Flags().StringVarP(&o.FlagPath, "filepath", "f", "", "attestation filepath")
cmd.Flags().StringVar(&o.FlagAttestationType, "type", "", `specify a predicate type (slsaprovenance|link|spdx|spdxjson|cyclonedx|vuln|custom) or an URI (default "custom")`)
cmd.Flags().StringVar(&o.FlagAttestationType, "type", "", `specify a predicate type URI`)
}

func (o *options) Run(ctx context.Context) error {
Expand Down

0 comments on commit fad643d

Please sign in to comment.