Skip to content

Commit

Permalink
fix issue 919
Browse files Browse the repository at this point in the history
  • Loading branch information
vaikas committed Oct 20, 2021
1 parent fc58838 commit 66b5fd4
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 18 deletions.
22 changes: 5 additions & 17 deletions cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"path/filepath"

Expand All @@ -37,7 +36,6 @@ import (
"github.com/sigstore/cosign/pkg/cosign/pivkey"
sigs "github.com/sigstore/cosign/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/dsse"
)

// VerifyAttestationCommand verifies a signature on a supplied container image
Expand All @@ -55,17 +53,6 @@ type VerifyAttestationCommand struct {
Policies []string
}

// DSSE messages contain the signature and payload in one object, but our interface expects a signature and payload
// This means we need to use one field and ignore the other. The DSSE verifier upstream uses the signature field and ignores
// The message field, but we want the reverse here.
type reverseDSSEVerifier struct {
signature.Verifier
}

func (w *reverseDSSEVerifier) VerifySignature(s io.Reader, m io.Reader, opts ...signature.VerifyOption) error {
return w.Verifier.VerifySignature(m, nil, opts...)
}

// Exec runs the verification command
func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (err error) {
if len(images) == 0 {
Expand All @@ -80,7 +67,6 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
if err != nil {
return errors.Wrap(err, "constructing client options")
}

co := &cosign.CheckOpts{
RegistryClientOpts: ociremoteOpts,
}
Expand Down Expand Up @@ -111,9 +97,11 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
return errors.Wrap(err, "initializing piv token verifier")
}
}

co.SigVerifier = &reverseDSSEVerifier{
Verifier: dsse.WrapVerifier(pubKey),
if pubKey != nil {
// TODO(vaikas): Should this be private and cosign just figures out
// how to wrap things. This would mean we need to pass more context, so
// just making it like this for now.
co.SigVerifier = cosign.NewReverseDSSEVerifier(pubKey)
}

for _, imageRef := range images {
Expand Down
30 changes: 29 additions & 1 deletion pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"strings"
"time"

Expand All @@ -38,6 +39,7 @@ import (
"github.com/sigstore/rekor/pkg/generated/client"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/dsse"
"github.com/sigstore/sigstore/pkg/signature/options"
)

Expand Down Expand Up @@ -65,6 +67,23 @@ type CheckOpts struct {
CertEmail string
}

// DSSE messages (Attestations) contain the signature and payload in one object, but our interface expects a signature and payload
// This means we need to use one field and ignore the other. The DSSE verifier upstream uses the signature field and ignores
// The message field, but we want the reverse here.
type reverseDSSEVerifier struct {
signature.Verifier
}

func NewReverseDSSEVerifier(v signature.Verifier) signature.Verifier {
return &reverseDSSEVerifier{
Verifier: dsse.WrapVerifier(v),
}
}

func (w *reverseDSSEVerifier) VerifySignature(s io.Reader, m io.Reader, opts ...signature.VerifyOption) error {
return w.Verifier.VerifySignature(m, nil, opts...)
}

// VerifySignatures does all the main cosign checks in a loop, returning the verified signatures.
// If there were no valid signatures, we return an error.
func VerifySignatures(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) {
Expand Down Expand Up @@ -154,7 +173,8 @@ func Verify(ctx context.Context, signedImgRef name.Reference, accessor Accessor,
if cert == nil {
return errors.New("no certificate found on signature")
}
pub, err := signature.LoadECDSAVerifier(cert.PublicKey.(*ecdsa.PublicKey), crypto.SHA256)
var pub signature.Verifier
pub, err = signature.LoadECDSAVerifier(cert.PublicKey.(*ecdsa.PublicKey), crypto.SHA256)
if err != nil {
return errors.Wrap(err, "invalid certificate found on signature")
}
Expand All @@ -167,6 +187,14 @@ func Verify(ctx context.Context, signedImgRef name.Reference, accessor Accessor,
if err != nil {
return err
}

// The fact that there's no signature (or empty rather), implies
// that this is an Attestation that we're verifying. So, we need
// to construct a Verifier that grabs the signature from the
// payload instead of the Signatures annotations.
if len(signature) == 0 {
pub = NewReverseDSSEVerifier(pub)
}
if err := pub.VerifySignature(bytes.NewReader(signature), bytes.NewReader(payload), options.WithContext(ctx)); err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/oci/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type SignedEntity interface {

// Attestations returns the set of attestations currently associated with this
// entity, or the empty equivalent if none are found.
// Attestations are just like a Signature, but they do not contain
// Base64Signature because it's baked into the payload.
Attestations() (Signatures, error)

// Attachment returns a named entity associated with this entity, or error if not found.
Expand Down
3 changes: 3 additions & 0 deletions pkg/oci/static/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ func NewSignature(payload []byte, b64sig string, opts ...Option) (oci.Signature,
}

// NewAttestation constructs a new oci.Signature from the provided options.
// Since Attestation is treated just like a Signature but the actual signature
// is baked into the payload, the Signature does not actually have
// the Base64Signature.
func NewAttestation(payload []byte, opts ...Option) (oci.Signature, error) {
return NewSignature(payload, "", opts...)
}
Expand Down

0 comments on commit 66b5fd4

Please sign in to comment.