Skip to content
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

Modernize attest statements #620

Merged
merged 6 commits into from
Jan 22, 2025
Merged
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
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
63 changes: 42 additions & 21 deletions internal/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,25 @@ 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 (
CommitRef = "refs/attestations/commits"
TreeRef = "refs/attestations/trees"
CommitRef = "refs/attestations/commits"
TreeRef = "refs/attestations/trees"
DigestTypeCommit = "gitCommit"
DigestTypeTree = "gitTree"
)

var (
Expand All @@ -57,18 +61,20 @@ var (
type rekorUpload func(ctx context.Context, rekorClient *rekorclient.Rekor, signature []byte, pemBytes []byte) (*models.LogEntryAnon, error)

type Attestor struct {
repo *git.Repository
sv *sign.SignerVerifier
rekorFn rekorUpload
config *gitsignconfig.Config
repo *git.Repository
sv *sign.SignerVerifier
rekorFn rekorUpload
config *gitsignconfig.Config
digestType string
}

func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload, config *gitsignconfig.Config) *Attestor {
func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload, config *gitsignconfig.Config, digestType string) *Attestor {
return &Attestor{
repo: repo,
sv: sv,
rekorFn: rekorFn,
config: config,
repo: repo,
sv: sv,
rekorFn: rekorFn,
config: config,
digestType: digestType,
}
}

Expand Down Expand Up @@ -111,7 +117,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 +232,32 @@ func encode(store storage.Storer, enc Encoder) (plumbing.Hash, error) {
return store.SetEncodedObject(obj)
}

func generateStatement(pred []byte, attType string, digestType string, sha plumbing.Hash) (*spb.Statement, error) {
sub := []*spb.ResourceDescriptor{{
Digest: map[string]string{digestType: 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, a.digestType, 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,6 +268,7 @@ func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte,
}

rekorHost, rekorBasePath := utils.StripURL(a.config.Rekor)

tc := &rekorclient.TransportConfig{
Host: rekorHost,
BasePath: rekorBasePath,
Expand Down
Loading
Loading