Skip to content

Commit

Permalink
Merge pull request #6 from openvex/attest
Browse files Browse the repository at this point in the history
Restore attestation capability after repo split
  • Loading branch information
puerco authored Jan 17, 2023
2 parents 747d599 + 50bb97d commit 377d414
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 19 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ go 1.19

require (
github.com/google/go-containerregistry v0.12.1
github.com/openvex/vex v0.1.1-0.20230110080744-b295df0b0ef1
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add
github.com/openvex/vex v0.1.1-0.20230117021350-78649647b8fb
github.com/owenrumney/go-sarif v1.1.1
github.com/secure-systems-lab/go-securesystemslib v0.4.0
github.com/sigstore/cosign v1.13.1
github.com/sigstore/sigstore v1.5.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
sigs.k8s.io/release-utils v0.7.3
Expand Down Expand Up @@ -129,7 +131,6 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/in-toto/in-toto-golang v0.3.4-0.20220709202702-fa494aaa0add // indirect
github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect
github.com/jhump/protoreflect v1.14.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
Expand Down Expand Up @@ -168,7 +169,6 @@ require (
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sigstore/fulcio v0.6.0 // indirect
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2 // indirect
github.com/sigstore/sigstore v1.5.0 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.8.2 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1037,8 +1037,8 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openvex/vex v0.1.1-0.20230110080744-b295df0b0ef1 h1:MlPltqDIi3Q2eshmLhJ7Hhvne4mR2lGnvb7z+rC5Akk=
github.com/openvex/vex v0.1.1-0.20230110080744-b295df0b0ef1/go.mod h1:I3ZjbXZAjc3jM+qCXhA3lu0neNmPEzpH8ZwAjfW4TG0=
github.com/openvex/vex v0.1.1-0.20230117021350-78649647b8fb h1:R0OZhJcNmtIVTRJJfSPcqsWkpOi0A60WMrnX+iNt5gg=
github.com/openvex/vex v0.1.1-0.20230117021350-78649647b8fb/go.mod h1:hyBd9x0no4IVW7VFP+wyVvqwX5mw7Lv5AhlN3owLhdY=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
Expand Down
37 changes: 31 additions & 6 deletions internal/cmd/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,31 @@ type attestOptions struct {
func addAttest(parentCmd *cobra.Command) {
opts := attestOptions{}
generateCmd := &cobra.Command{
Short: fmt.Sprintf("%s attest: generate a VEX attestation", appname),
Long: ``,
Short: fmt.Sprintf("%s attest: generate a VEX attestation", appname),
Long: fmt.Sprintf(`%s attest: generate an VEX attestation
The attach subcommand lets users wrap OpenVEX documents in in-toto attestations.
Attestations generated by %s can be signed with sigstore and attached to container
images stored in an OCI registry.
In its simplest form, %s will create an attestation from an OpenVEX file and
write it to stdout:
%s attest data.vex.json
If the vex document defines any subjects, %s will read the product entries from
the document and transfer them to the attestation's subjects section (see the
in-toto documentation for more info).
Passing the --sign flag will trigger the cosign signing flow, either asking for
credentials from the user or trying to get them from the environment:
%s attest --sign data.vex.json
Further positional arguments are considered to be container images and will be
added to the attestation as subjects
`, appname, appname, appname, appname, appname, appname),
Use: "attest",
SilenceUsage: false,
SilenceErrors: false,
Expand Down Expand Up @@ -59,17 +82,19 @@ func addAttest(parentCmd *cobra.Command) {
},
}

generateCmd.PersistentFlags().BoolVar(
generateCmd.PersistentFlags().BoolVarP(
&opts.attach,
"attach",
true,
"a",
false,
"attach the generated attestation to an image",
)

generateCmd.PersistentFlags().BoolVar(
generateCmd.PersistentFlags().BoolVarP(
&opts.sign,
"sign",
true,
"s",
false,
"sign the attestation with sigstore",
)

Expand Down
125 changes: 125 additions & 0 deletions pkg/attestation/attestation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
Copyright 2022 Chainguard, Inc.
SPDX-License-Identifier: Apache-2.0
*/

package attestation

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"strings"
"time"

"github.com/google/go-containerregistry/pkg/crane"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ovattest "github.com/openvex/vex/pkg/attestation"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
"github.com/sigstore/sigstore/pkg/signature/dsse"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
)

type Attestation struct {
signedData []byte `json:"-"`
ovattest.Attestation
Signed bool `json:"-"`
}

func New() *Attestation {
openVexAttestation := ovattest.New()
return &Attestation{
Attestation: *openVexAttestation,
}
}

// Sign the attestation
func (att *Attestation) Sign() error {
ctx := context.Background()
var timeout time.Duration /// TODO move to options
var certPath, certChainPath string
ko := options.KeyOpts{
// KeyRef: s.options.PrivateKeyPath,
// IDToken: identityToken,
FulcioURL: options.DefaultFulcioURL,
RekorURL: options.DefaultRekorURL,
OIDCIssuer: options.DefaultOIDCIssuerURL,
OIDCClientID: "sigstore",

InsecureSkipFulcioVerify: false,
SkipConfirmation: true,
// FulcioAuthFlow: "",
}

if timeout != 0 {
var cancelFn context.CancelFunc
ctx, cancelFn = context.WithTimeout(ctx, timeout)
defer cancelFn()
}

sv, err := sign.SignerFromKeyOpts(ctx, certPath, certChainPath, ko)
if err != nil {
return fmt.Errorf("getting signer: %w", err)
}
defer sv.Close()

// Wrap the attestation in the DSSE envelope
wrapped := dsse.WrapSigner(sv, "application/vnd.in-toto+json")

var b bytes.Buffer
if err := att.ToJSON(&b); err != nil {
return fmt.Errorf("serializing attestation to json: %w", err)
}

signedPayload, err := wrapped.SignMessage(
bytes.NewReader(b.Bytes()), signatureoptions.WithContext(ctx),
)
if err != nil {
return fmt.Errorf("signing attestation: %w", err)
}

att.Signed = true
att.signedData = signedPayload
return nil
}

func (att *Attestation) AddImageSubjects(imageRefs []string) error {
subs := []intoto.Subject{}
for _, refString := range imageRefs {
digest, err := crane.Digest(refString)
if err != nil {
return fmt.Errorf("getting image digest: %w", err)
}
s := intoto.Subject{
Name: refString,
Digest: map[string]string{"sha256": strings.TrimPrefix(digest, "sha256:")},
}

subs = append(subs, s)
}

if err := att.AddSubjects(subs); err != nil {
return fmt.Errorf("adding image subjects to attestation: %w", err)
}

return nil
}

// ToJSON intercepts the openves to json call and if the attestation is signed
// writes the signed data to io.Writer w instead of the original attestation.
func (att *Attestation) ToJSON(w io.Writer) error {
if !att.Signed {
return att.Attestation.ToJSON(w)
}
if len(att.signedData) == 0 {
return errors.New("consistency error: attestation is signed but data is empty")
}

if _, err := w.Write(att.signedData); err != nil {
return fmt.Errorf("writing signed attestation: %w", err)
}
return nil
}
15 changes: 8 additions & 7 deletions pkg/ctl/ctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
"context"
"fmt"

"github.com/openvex/vex/pkg/attestation"
"github.com/openvex/vex/pkg/sarif"
"github.com/openvex/vex/pkg/vex"

"github.com/openvex/vexctl/pkg/attestation"
)

type VexCtl struct {
Expand Down Expand Up @@ -71,18 +72,18 @@ func (vexctl *VexCtl) Attest(vexDataPath string, imageRefs []string) (*attestati
return nil, fmt.Errorf("adding image references to attestation")
}

return att, nil
}

// Attach attaches an attestation to a list of images
func (vexctl *VexCtl) Attach(ctx context.Context, att *attestation.Attestation, imageRefs []string) (err error) {
// Sign the attestation
if vexctl.Options.Sign {
if err := att.Sign(); err != nil {
return fmt.Errorf("signing attestation: %w", err)
return att, fmt.Errorf("signing attestation: %w", err)
}
}

return att, nil
}

// Attach attaches an attestation to a list of images
func (vexctl *VexCtl) Attach(ctx context.Context, att *attestation.Attestation, imageRefs []string) (err error) {
for _, ref := range imageRefs {
if err := vexctl.impl.Attach(ctx, att, ref); err != nil {
return fmt.Errorf("attaching attestation: %w", err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/ctl/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ import (
"github.com/sirupsen/logrus"
"sigs.k8s.io/release-utils/util"

"github.com/openvex/vex/pkg/attestation"
"github.com/openvex/vex/pkg/sarif"
"github.com/openvex/vex/pkg/vex"
"github.com/openvex/vexctl/pkg/attestation"
)

const IntotoPayloadType = "application/vnd.in-toto+json"
Expand Down

0 comments on commit 377d414

Please sign in to comment.