diff --git a/cmd/cosign/cli/options/verify.go b/cmd/cosign/cli/options/verify.go index 4fe8c0d091d5..f2bcb05d3f43 100644 --- a/cmd/cosign/cli/options/verify.go +++ b/cmd/cosign/cli/options/verify.go @@ -221,7 +221,7 @@ func (o *VerifyBlobAttestationOptions) AddFlags(cmd *cobra.Command) { "path to bundle FILE") cmd.Flags().BoolVar(&o.CheckClaims, "check-claims", true, - "whether to check the claims found") + "if true, verifies the provided blob's sha256 digest exists as an in-toto subject within the attestation. If false, only the DSSE envelope is verified.") cmd.Flags().StringVar(&o.RFC3161TimestampPath, "rfc3161-timestamp", "", "path to RFC3161 timestamp FILE") diff --git a/cmd/cosign/cli/verify.go b/cmd/cosign/cli/verify.go index 0522d0aa4c3c..28d6d2e0e988 100644 --- a/cmd/cosign/cli/verify.go +++ b/cmd/cosign/cli/verify.go @@ -326,7 +326,7 @@ The blob may be specified as a path to a file.`, `, - Args: cobra.ExactArgs(1), + Args: cobra.MaximumNArgs(1), PersistentPreRun: options.BindViper, RunE: func(cmd *cobra.Command, args []string) error { ko := options.KeyOpts{ @@ -356,10 +356,15 @@ The blob may be specified as a path to a file.`, Offline: o.CommonVerifyOptions.Offline, IgnoreTlog: o.CommonVerifyOptions.IgnoreTlog, } - if len(args) != 1 { + // We only use the blob if we are checking claims. + if len(args) == 0 && o.CheckClaims { return fmt.Errorf("no path to blob passed in, run `cosign verify-blob-attestation -h` for more help") } - return v.Exec(cmd.Context(), args[0]) + var path string + if len(args) > 0 { + path = args[0] + } + return v.Exec(cmd.Context(), path) }, } diff --git a/cmd/cosign/cli/verify/verify_blob_attestation.go b/cmd/cosign/cli/verify/verify_blob_attestation.go index 4426e596f3e9..d629a9c361d8 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation.go @@ -108,26 +108,26 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st Offline: c.Offline, IgnoreTlog: c.IgnoreTlog, } + var h v1.Hash if c.CheckClaims { - co.ClaimVerifier = cosign.IntotoSubjectClaimVerifier - } - - // Get the actual digest of the blob - var payload internal.HashReader - f, err := os.Open(filepath.Clean(artifactPath)) - if err != nil { - return err - } - defer f.Close() + // Get the actual digest of the blob + var payload internal.HashReader + f, err := os.Open(filepath.Clean(artifactPath)) + if err != nil { + return err + } + defer f.Close() - payload = internal.NewHashReader(f, sha256.New()) - if _, err := io.ReadAll(&payload); err != nil { - return err - } - digest := payload.Sum(nil) - h := v1.Hash{ - Hex: hex.EncodeToString(digest), - Algorithm: "sha256", + payload = internal.NewHashReader(f, sha256.New()) + if _, err := io.ReadAll(&payload); err != nil { + return err + } + digest := payload.Sum(nil) + h = v1.Hash{ + Hex: hex.EncodeToString(digest), + Algorithm: "sha256", + } + co.ClaimVerifier = cosign.IntotoSubjectClaimVerifier } // Set up TSA, Fulcio roots and tlog public keys and clients. diff --git a/cmd/cosign/cli/verify/verify_blob_attestation_test.go b/cmd/cosign/cli/verify/verify_blob_attestation_test.go index 997442f43d6b..35c9940d86d2 100644 --- a/cmd/cosign/cli/verify/verify_blob_attestation_test.go +++ b/cmd/cosign/cli/verify/verify_blob_attestation_test.go @@ -124,3 +124,58 @@ func TestVerifyBlobAttestation(t *testing.T) { }) } } + +func TestVerifyBlobAttestationNoCheckClaims(t *testing.T) { + ctx := context.Background() + td := t.TempDir() + defer os.RemoveAll(td) + + blobPath := writeBlobFile(t, td, blobContents, "blob") + anotherBlobPath := writeBlobFile(t, td, anotherBlobContents, "other-blob") + keyRef := writeBlobFile(t, td, pubkey, "cosign.pub") + + tests := []struct { + description string + blobPath string + signature string + }{ + { + description: "verify a predicate", + blobPath: blobPath, + signature: blobSLSAProvenanceSignature, + }, { + description: "verify a predicate no path", + signature: blobSLSAProvenanceSignature, + }, { + description: "verify a predicate with another blob path", + signature: blobSLSAProvenanceSignature, + // This works because we're not checking the claims. It doesn't matter what we put in here - it should pass so long as the DSSE signagure can be verified. + blobPath: anotherBlobPath, + }, { + description: "verify a predicate with /dev/null", + signature: blobSLSAProvenanceSignature, + blobPath: "/dev/null", + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + decodedSig, err := base64.StdEncoding.DecodeString(test.signature) + if err != nil { + t.Fatal(err) + } + sigRef := writeBlobFile(t, td, string(decodedSig), "signature") + + cmd := VerifyBlobAttestationCommand{ + KeyOpts: options.KeyOpts{KeyRef: keyRef}, + SignaturePath: sigRef, + IgnoreTlog: true, + CheckClaims: false, + PredicateType: "slsaprovenance", + } + if err := cmd.Exec(ctx, test.blobPath); err != nil { + t.Fatalf("verifyBlobAttestation()= %v", err) + } + }) + } +} diff --git a/doc/cosign_verify-blob-attestation.md b/doc/cosign_verify-blob-attestation.md index f9ba60e25dd4..aee0633ccd1f 100644 --- a/doc/cosign_verify-blob-attestation.md +++ b/doc/cosign_verify-blob-attestation.md @@ -40,7 +40,7 @@ cosign verify-blob-attestation [flags] --certificate-identity-regexp string A regular expression alternative to --certificate-identity. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either --certificate-identity or --certificate-identity-regexp must be set for keyless flows. --certificate-oidc-issuer string The OIDC issuer expected in a valid Fulcio certificate, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth. Either --certificate-oidc-issuer or --certificate-oidc-issuer-regexp must be set for keyless flows. --certificate-oidc-issuer-regexp string A regular expression alternative to --certificate-oidc-issuer. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either --certificate-oidc-issuer or --certificate-oidc-issuer-regexp must be set for keyless flows. - --check-claims whether to check the claims found (default true) + --check-claims if true, verifies the provided blob's sha256 digest exists as an in-toto subject within the attestation. If false, only the DSSE envelope is verified. (default true) -h, --help help for verify-blob-attestation --insecure-ignore-sct when set, verification will not check that a certificate contains an embedded SCT, a proof of inclusion in a certificate transparency log --insecure-ignore-tlog ignore transparency log verification, to be used when an artifact signature has not been uploaded to the transparency log. Artifacts cannot be publicly verified when not included in a log