diff --git a/cmd/cosign/cli/options/verify.go b/cmd/cosign/cli/options/verify.go index f2bcb05d3f43..98348a412192 100644 --- a/cmd/cosign/cli/options/verify.go +++ b/cmd/cosign/cli/options/verify.go @@ -45,6 +45,7 @@ type VerifyOptions struct { Attachment string Output string SignatureRef string + PayloadRef string LocalImage bool CommonVerifyOptions CommonVerifyOptions @@ -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'") } diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index 28d6d2e0e988..c3d19186042e 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -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, diff --git a/cmd/cosign/cli/verify/verify.go b/cmd/cosign/cli/verify/verify.go index 73de8b4aba76..fe9ac258b6af 100644 --- a/cmd/cosign/cli/verify/verify.go +++ b/cmd/cosign/cli/verify/verify.go @@ -70,6 +70,7 @@ type VerifyCommand struct { Attachment string Annotations sigs.AnnotationsMap SignatureRef string + PayloadRef string HashAlgorithm crypto.Hash LocalImage bool NameOptions []name.Option @@ -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, diff --git a/doc/cosign_dockerfile_verify.md b/doc/cosign_dockerfile_verify.md index 6ad78a9480cf..a5a1d6c8fd73 100644 --- a/doc/cosign_dockerfile_verify.md +++ b/doc/cosign_dockerfile_verify.md @@ -75,6 +75,7 @@ cosign dockerfile verify [flags] --local-image whether the specified image is a path to an image saved locally via 'cosign save' --offline only allow offline verification -o, --output string output format for the signing image information (json|text) (default "json") + --payload string payload path or remote URL --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sct string path to a detached Signed Certificate Timestamp, formatted as a RFC6962 AddChainResponse struct. If a certificate contains an SCT, verification will check both the detached and embedded SCTs. --signature string signature content or path or remote URL diff --git a/doc/cosign_manifest_verify.md b/doc/cosign_manifest_verify.md index 5ac17bbf0c24..fc9565b3d8cf 100644 --- a/doc/cosign_manifest_verify.md +++ b/doc/cosign_manifest_verify.md @@ -69,6 +69,7 @@ cosign manifest verify [flags] --local-image whether the specified image is a path to an image saved locally via 'cosign save' --offline only allow offline verification -o, --output string output format for the signing image information (json|text) (default "json") + --payload string payload path or remote URL --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sct string path to a detached Signed Certificate Timestamp, formatted as a RFC6962 AddChainResponse struct. If a certificate contains an SCT, verification will check both the detached and embedded SCTs. --signature string signature content or path or remote URL diff --git a/doc/cosign_verify.md b/doc/cosign_verify.md index 13e77b0616c3..1ea9d603f19e 100644 --- a/doc/cosign_verify.md +++ b/doc/cosign_verify.md @@ -88,6 +88,7 @@ cosign verify [flags] --local-image whether the specified image is a path to an image saved locally via 'cosign save' --offline only allow offline verification -o, --output string output format for the signing image information (json|text) (default "json") + --payload string payload path or remote URL --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sct string path to a detached Signed Certificate Timestamp, formatted as a RFC6962 AddChainResponse struct. If a certificate contains an SCT, verification will check both the detached and embedded SCTs. --signature string signature content or path or remote URL diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index cb7dea39e586..4072b5f3f499 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -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" ) @@ -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. Either none or both of SignatureRef and PayloadRef must be specified. SignatureRef string + // PayloadRef is a reference to the paylaod file. Either none or both of SignatureRef and PayloadRef must be specified. + PayloadRef string // Identities is an array of Identity (Subject, Issuer) matchers that have // to be met for the signature to ve valid. @@ -503,7 +504,10 @@ func VerifyImageSignatures(ctx context.Context, signedImgRef name.Reference, co 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(sigRef, co.PayloadRef) if err != nil { return nil, false, err } @@ -769,7 +773,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(sigRef string, payloadRef string) (oci.Signatures, error) { var b64sig string targetSig, err := blob.LoadFileOrURL(sigRef) if err != nil { @@ -787,13 +791,7 @@ 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() - + payload, err := blob.LoadFileOrURL(payloadRef) if err != nil { return nil, err } @@ -1353,7 +1351,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(sigRef, co.PayloadRef) if err != nil { return nil, false, err } diff --git a/test/e2e_test.ps1 b/test/e2e_test.ps1 index 9d2973c447db..66e4c30edd7e 100644 --- a/test/e2e_test.ps1 +++ b/test/e2e_test.ps1 @@ -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 diff --git a/test/e2e_test_secrets_kms.sh b/test/e2e_test_secrets_kms.sh index 55c81e55b2bf..27a1aab58c9e 100755 --- a/test/e2e_test_secrets_kms.sh +++ b/test/e2e_test_secrets_kms.sh @@ -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?