Skip to content

Commit

Permalink
Allow the payload to be provided with a provided signature in verific…
Browse files Browse the repository at this point in the history
…ation

... and warn if that's not the case.

The signature signs the payload; it makes no sense for the user
to provide the signature but not the payload - it would effectively
force cosign to generate a byte-for-byte identical (and, currently,
undesirable) payload forever.

Still, for compatibility, continue to accept such invocations,
but trigger a warning.

Signed-off-by: Miloslav Trmač <[email protected]>
  • Loading branch information
mtrmac committed Mar 13, 2023
1 parent b9c8eb5 commit b68e2f7
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 18 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type VerifyOptions struct {
Attachment string
Output string
SignatureRef string
PayloadRef string
LocalImage bool

CommonVerifyOptions CommonVerifyOptions
Expand Down Expand Up @@ -85,6 +86,9 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.SignatureRef, "signature", "",
"signature content or path or remote URL")

cmd.Flags().StringVar(&o.PayloadRef, "payload", "",
"payload path or remote URL")

cmd.Flags().BoolVar(&o.LocalImage, "local-image", false,
"whether the specified image is a path to an image saved locally via 'cosign save'")
}
Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ against the transparency log.`,
Annotations: annotations,
HashAlgorithm: hashAlgorithm,
SignatureRef: o.SignatureRef,
PayloadRef: o.PayloadRef,
LocalImage: o.LocalImage,
Offline: o.CommonVerifyOptions.Offline,
TSACertChainPath: o.CommonVerifyOptions.TSACertChainPath,
Expand Down
2 changes: 2 additions & 0 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type VerifyCommand struct {
Attachment string
Annotations sigs.AnnotationsMap
SignatureRef string
PayloadRef string
HashAlgorithm crypto.Hash
LocalImage bool
NameOptions []name.Option
Expand Down Expand Up @@ -119,6 +120,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
CertGithubWorkflowRef: c.CertGithubWorkflowRef,
IgnoreSCT: c.IgnoreSCT,
SignatureRef: c.SignatureRef,
PayloadRef: c.PayloadRef,
Identities: identities,
Offline: c.Offline,
IgnoreTlog: c.IgnoreTlog,
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.

38 changes: 24 additions & 14 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ 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"
tsaverification "github.com/sigstore/timestamp-authority/pkg/verification"
)

Expand Down Expand Up @@ -121,8 +120,10 @@ type CheckOpts struct {
// It is a map from log id to LogIDMetadata. It is a map from LogID to crypto.PublicKey. LogID is derived from the PublicKey (see RFC 6962 S3.2).
CTLogPubKeys *TrustedTransparencyLogPubKeys

// SignatureRef is the reference to the signature file
// SignatureRef is the reference to the signature file. PayloadRef should always be specified as well (though it’s possible for a _some_ signatures to be verified without it, with a warning).
SignatureRef string
// PayloadRef is a reference to the payload file. Applicable only if SignatureRef is set.
PayloadRef string

// Identities is an array of Identity (Subject, Issuer) matchers that have
// to be met for the signature to ve valid.
Expand Down Expand Up @@ -509,7 +510,7 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co
return nil, false, err
}
} else {
sigs, err = loadSignatureFromFile(sigRef, signedImgRef, co)
sigs, err = loadSignatureFromFile(ctx, sigRef, signedImgRef, co)
if err != nil {
return nil, false, err
}
Expand Down Expand Up @@ -782,7 +783,7 @@ func VerifyImageSignature(ctx context.Context, sig oci.Signature, h v1.Hash, co
return verifyInternal(ctx, sig, h, verifyOCISignature, co)
}

func loadSignatureFromFile(sigRef string, signedImgRef name.Reference, co *CheckOpts) (oci.Signatures, error) {
func loadSignatureFromFile(ctx context.Context, sigRef string, signedImgRef name.Reference, co *CheckOpts) (oci.Signatures, error) {
var b64sig string
targetSig, err := blob.LoadFileOrURL(sigRef)
if err != nil {
Expand All @@ -800,15 +801,21 @@ func loadSignatureFromFile(sigRef string, signedImgRef name.Reference, co *Check
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
var payload []byte
if co.PayloadRef != "" {
payload, err = blob.LoadFileOrURL(co.PayloadRef)
if err != nil {
return nil, err
}
} else {
digest, err := ociremote.ResolveDigest(signedImgRef, co.RegistryClientOpts...)
if err != nil {
return nil, err
}
payload, err = ObsoletePayload(ctx, digest)
if err != nil {
return nil, err
}
}

sig, err := static.NewSignature(payload, b64sig)
Expand Down Expand Up @@ -1366,7 +1373,10 @@ func verifyImageSignaturesExperimentalOCI(ctx context.Context, signedImgRef name
return nil, false, err
}
} else {
sigs, err = loadSignatureFromFile(sigRef, signedImgRef, co)
if co.PayloadRef == "" {
return nil, false, errors.New("payload is required with a manually-provided signature")
}
sigs, err = loadSignatureFromFile(ctx, sigRef, signedImgRef, co)
if err != nil {
return nil, false, err
}
Expand Down
4 changes: 2 additions & 2 deletions test/e2e_test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ $signing_key = "cosign.key"
$verification_key = "cosign.pub"

$test_img = "ghcr.io/distroless/static"
Write-Output $pass | .\cosign.exe sign --key $signing_key --output-signature interactive.sig --tlog-upload=false $test_img
.\cosign.exe verify --key $verification_key --signature interactive.sig --insecure-ignore-tlog=true $test_img
Write-Output $pass | .\cosign.exe sign --key $signing_key --output-signature interactive.sig --output-payload interactive.payload --tlog-upload=false $test_img
.\cosign.exe verify --key $verification_key --signature interactive.sig --payload interactive.payload --insecure-ignore-tlog=true $test_img

Pop-Location

Expand Down
4 changes: 2 additions & 2 deletions test/e2e_test_secrets_kms.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ unset COSIGN_REPOSITORY
stdin_password=${COSIGN_PASSWORD}
unset COSIGN_PASSWORD
(crane delete $(./cosign triangulate $img)) || true
echo $stdin_password | ./cosign sign --key ${signing_key} --output-signature interactive.sig $img
COSIGN_KEY=${verification_key} COSIGN_SIGNATURE=interactive.sig ./cosign verify $img
echo $stdin_password | ./cosign sign --key ${signing_key} --output-signature interactive.sig --output-payload interactive.payload $img
COSIGN_KEY=${verification_key} COSIGN_SIGNATURE=interactive.sig ./cosign verify --payload interactive.payload $img
export COSIGN_PASSWORD=${stdin_password}

# What else needs auth?
Expand Down

0 comments on commit b68e2f7

Please sign in to comment.