Skip to content

Commit

Permalink
feat: support signature file in verify cmd (#1068)
Browse files Browse the repository at this point in the history
* feat: support signature file in verify cmd

Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
Co-authored-by: Furkan Türkal <furkan.turkal@trendyol.com>
Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>

* feat: updates according to code review

Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>

Co-authored-by: Furkan Türkal <furkan.turkal@trendyol.com>
  • Loading branch information
developer-guy and Dentrax committed Dec 1, 2021
1 parent ec00f69 commit 54fb569
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 21 deletions.
14 changes: 9 additions & 5 deletions cmd/cosign/cli/options/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import (

// VerifyOptions is the top level wrapper for the `verify` command.
type VerifyOptions struct {
Key string
CertEmail string // TODO: merge into fulcio option as read mode?
CheckClaims bool
Attachment string
Output string
Key string
CertEmail string // TODO: merge into fulcio option as read mode?
CheckClaims bool
Attachment string
Output string
SignatureRef string

SecurityKey SecurityKeyOptions
Rekor RekorOptions
Expand Down Expand Up @@ -59,6 +60,9 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) {

cmd.Flags().StringVarP(&o.Output, "output", "o", "json",
"output format for the signing image information (json|text)")

cmd.Flags().StringVar(&o.SignatureRef, "signature", "",
"signature content or path or remote URL")
}

// VerifyAttestationOptions is the top level wrapper for the `verify attestation` command.
Expand Down
4 changes: 2 additions & 2 deletions cmd/cosign/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ against the transparency log.`,
RekorURL: o.Rekor.URL,
Attachment: o.Attachment,
Annotations: annotations,

HashAlgorithm: hashAlgorithm,
HashAlgorithm: hashAlgorithm,
SignatureRef: o.SignatureRef,
}

return v.Exec(cmd.Context(), args)
Expand Down
21 changes: 11 additions & 10 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ import (
// nolint
type VerifyCommand struct {
options.RegistryOptions
CheckClaims bool
KeyRef string
CertEmail string
Sk bool
Slot string
Output string
RekorURL string
Attachment string
Annotations sigs.AnnotationsMap

CheckClaims bool
KeyRef string
CertEmail string
Sk bool
Slot string
Output string
RekorURL string
Attachment string
Annotations sigs.AnnotationsMap
SignatureRef string
HashAlgorithm crypto.Hash
}

Expand Down Expand Up @@ -84,6 +84,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
Annotations: c.Annotations.Annotations,
RegistryClientOpts: ociremoteOpts,
CertEmail: c.CertEmail,
SignatureRef: c.SignatureRef,
}
if c.CheckClaims {
co.ClaimVerifier = cosign.SimpleClaimVerifier
Expand Down
1 change: 1 addition & 0 deletions doc/cosign_dockerfile_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_manifest_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 69 additions & 4 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strings"
"time"

"github.com/sigstore/cosign/pkg/blob"
"github.com/sigstore/cosign/pkg/oci/static"

"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand All @@ -43,6 +47,7 @@ import (
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/dsse"
"github.com/sigstore/sigstore/pkg/signature/options"
sigPayload "github.com/sigstore/sigstore/pkg/signature/payload"
)

// CheckOpts are the options for checking signatures.
Expand All @@ -67,6 +72,9 @@ type CheckOpts struct {
RootCerts *x509.CertPool
// CertEmail is the email expected for a certificate to be valid. The empty string means any certificate can be valid.
CertEmail string

// SignatureRef is the reference to the signature file
SignatureRef string
}

func getSignedEntity(signedImgRef name.Reference, regClientOpts []ociremote.Option) (oci.SignedEntity, v1.Hash, error) {
Expand Down Expand Up @@ -186,6 +194,15 @@ func tlogValidateCertificate(ctx context.Context, rekorClient *client.Rekor, sig
return checkExpiry(cert, time.Unix(*e.IntegratedTime, 0))
}

type fakeOCISignatures struct {
oci.Signatures
signatures []oci.Signature
}

func (fos *fakeOCISignatures) Get() ([]oci.Signature, error) {
return fos.signatures, nil
}

// 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 VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedSignatures []oci.Signature, bundleVerified bool, err error) {
Expand All @@ -196,15 +213,25 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co

// TODO(mattmoor): We could implement recursive verification if we just wrapped
// most of the logic below here in a call to mutate.Map

se, h, err := getSignedEntity(signedImgRef, co.RegistryClientOpts)
if err != nil {
return nil, false, err
}
sigs, err := se.Signatures()
if err != nil {
return nil, false, err

var sigs oci.Signatures
sigRef := co.SignatureRef
if sigRef == "" {
sigs, err = se.Signatures()
if err != nil {
return nil, false, err
}
} else {
sigs, err = loadSignatureFromFile(sigRef, signedImgRef, co)
if err != nil {
return nil, false, err
}
}

sl, err := sigs.Get()
if err != nil {
return nil, false, err
Expand Down Expand Up @@ -281,6 +308,44 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co
return checkedSignatures, bundleVerified, nil
}

func loadSignatureFromFile(sigRef string, signedImgRef name.Reference, co *CheckOpts) (oci.Signatures, error) {
var b64sig string
targetSig, err := blob.LoadFileOrURL(sigRef)
if err != nil {
if !os.IsNotExist(err) {
return nil, err
}
targetSig = []byte(sigRef)
}

_, err = base64.StdEncoding.DecodeString(string(targetSig))

if err == nil {
b64sig = string(targetSig)
} else {
b64sig = base64.StdEncoding.EncodeToString(targetSig)
}

digest, err := ociremote.ResolveDigest(signedImgRef, co.RegistryClientOpts...)
if err != nil {
return nil, err
}

payload, err := (&sigPayload.Cosign{Image: digest}).MarshalJSON()

if err != nil {
return nil, err
}

sig, err := static.NewSignature(payload, b64sig)
if err != nil {
return nil, err
}
return &fakeOCISignatures{
signatures: []oci.Signature{sig},
}, nil
}

// VerifyAttestations does all the main cosign checks in a loop, returning the verified attestations.
// If there were no valid attestations, we return an error.
func VerifyImageAttestations(ctx context.Context, signedImgRef name.Reference, co *CheckOpts) (checkedAttestations []oci.Signature, bundleVerified bool, err error) {
Expand Down

0 comments on commit 54fb569

Please sign in to comment.