github.com/sigstore/cosign@v1.13.6/test/e2e_test.go (about)

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  //go:build e2e
    17  // +build e2e
    18  
    19  package test
    20  
    21  import (
    22  	"bytes"
    23  	"context"
    24  	"crypto"
    25  	"encoding/base64"
    26  	"encoding/json"
    27  	"fmt"
    28  	"io"
    29  	"net/http/httptest"
    30  	"net/url"
    31  	"os"
    32  	"path"
    33  	"path/filepath"
    34  	"testing"
    35  	"time"
    36  
    37  	"github.com/google/go-cmp/cmp"
    38  	"github.com/google/go-containerregistry/pkg/authn"
    39  	"github.com/google/go-containerregistry/pkg/name"
    40  	"github.com/google/go-containerregistry/pkg/registry"
    41  	"github.com/google/go-containerregistry/pkg/v1/random"
    42  	"github.com/google/go-containerregistry/pkg/v1/remote"
    43  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    44  	k8s "k8s.io/client-go/kubernetes"
    45  	"k8s.io/client-go/tools/clientcmd"
    46  
    47  	// Initialize all known client auth plugins
    48  	_ "k8s.io/client-go/plugin/pkg/client/auth"
    49  
    50  	"github.com/sigstore/cosign/cmd/cosign/cli"
    51  	"github.com/sigstore/cosign/cmd/cosign/cli/attach"
    52  	"github.com/sigstore/cosign/cmd/cosign/cli/attest"
    53  	"github.com/sigstore/cosign/cmd/cosign/cli/download"
    54  	"github.com/sigstore/cosign/cmd/cosign/cli/generate"
    55  	"github.com/sigstore/cosign/cmd/cosign/cli/options"
    56  	"github.com/sigstore/cosign/cmd/cosign/cli/publickey"
    57  	"github.com/sigstore/cosign/cmd/cosign/cli/sign"
    58  	"github.com/sigstore/cosign/cmd/cosign/cli/upload"
    59  	cliverify "github.com/sigstore/cosign/cmd/cosign/cli/verify"
    60  	"github.com/sigstore/cosign/pkg/cosign"
    61  	"github.com/sigstore/cosign/pkg/cosign/env"
    62  	"github.com/sigstore/cosign/pkg/cosign/kubernetes"
    63  	cremote "github.com/sigstore/cosign/pkg/cosign/remote"
    64  	"github.com/sigstore/cosign/pkg/oci/mutate"
    65  	ociremote "github.com/sigstore/cosign/pkg/oci/remote"
    66  	"github.com/sigstore/cosign/pkg/sget"
    67  	sigs "github.com/sigstore/cosign/pkg/signature"
    68  	"github.com/sigstore/sigstore/pkg/signature/payload"
    69  )
    70  
    71  const (
    72  	serverEnv = "REKOR_SERVER"
    73  	rekorURL  = "https://rekor.sigstore.dev"
    74  )
    75  
    76  var keyPass = []byte("hello")
    77  
    78  var passFunc = func(_ bool) ([]byte, error) {
    79  	return keyPass, nil
    80  }
    81  
    82  var verify = func(keyRef, imageRef string, checkClaims bool, annotations map[string]interface{}, attachment string) error {
    83  	cmd := cliverify.VerifyCommand{
    84  		KeyRef:        keyRef,
    85  		RekorURL:      rekorURL,
    86  		CheckClaims:   checkClaims,
    87  		Annotations:   sigs.AnnotationsMap{Annotations: annotations},
    88  		Attachment:    attachment,
    89  		HashAlgorithm: crypto.SHA256,
    90  	}
    91  
    92  	args := []string{imageRef}
    93  
    94  	return cmd.Exec(context.Background(), args)
    95  }
    96  
    97  // Used to verify local images stored on disk
    98  var verifyLocal = func(keyRef, path string, checkClaims bool, annotations map[string]interface{}, attachment string) error {
    99  	cmd := cliverify.VerifyCommand{
   100  		KeyRef:        keyRef,
   101  		CheckClaims:   checkClaims,
   102  		Annotations:   sigs.AnnotationsMap{Annotations: annotations},
   103  		Attachment:    attachment,
   104  		HashAlgorithm: crypto.SHA256,
   105  		LocalImage:    true,
   106  	}
   107  
   108  	args := []string{path}
   109  
   110  	return cmd.Exec(context.Background(), args)
   111  }
   112  
   113  var ro = &options.RootOptions{Timeout: options.DefaultTimeout}
   114  
   115  func TestSignVerify(t *testing.T) {
   116  	repo, stop := reg(t)
   117  	defer stop()
   118  	td := t.TempDir()
   119  
   120  	imgName := path.Join(repo, "cosign-e2e")
   121  
   122  	_, _, cleanup := mkimage(t, imgName)
   123  	defer cleanup()
   124  
   125  	_, privKeyPath, pubKeyPath := keypair(t, td)
   126  
   127  	ctx := context.Background()
   128  	// Verify should fail at first
   129  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
   130  	// So should download
   131  	mustErr(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   132  
   133  	// Now sign the image
   134  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   135  	so := options.SignOptions{
   136  		Upload: true,
   137  	}
   138  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   139  
   140  	// Now verify and download should work!
   141  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   142  	must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   143  
   144  	// Look for a specific annotation
   145  	mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t)
   146  
   147  	so.AnnotationOptions = options.AnnotationOptions{
   148  		Annotations: []string{"foo=bar"},
   149  	}
   150  	// Sign the image with an annotation
   151  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   152  
   153  	// It should match this time.
   154  	must(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t)
   155  
   156  	// But two doesn't work
   157  	mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar", "baz": "bat"}, ""), t)
   158  }
   159  
   160  func TestSignVerifyClean(t *testing.T) {
   161  	repo, stop := reg(t)
   162  	defer stop()
   163  	td := t.TempDir()
   164  
   165  	imgName := path.Join(repo, "cosign-e2e")
   166  
   167  	_, _, _ = mkimage(t, imgName)
   168  
   169  	_, privKeyPath, pubKeyPath := keypair(t, td)
   170  
   171  	ctx := context.Background()
   172  
   173  	// Now sign the image
   174  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   175  	so := options.SignOptions{
   176  		Upload: true,
   177  	}
   178  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   179  
   180  	// Now verify and download should work!
   181  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   182  	must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   183  
   184  	// Now clean signature from the given image
   185  	must(cli.CleanCmd(ctx, options.RegistryOptions{}, "all", imgName, true), t)
   186  
   187  	// It doesn't work
   188  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
   189  }
   190  
   191  func TestImportSignVerifyClean(t *testing.T) {
   192  
   193  	repo, stop := reg(t)
   194  	defer stop()
   195  	td := t.TempDir()
   196  
   197  	imgName := path.Join(repo, "cosign-e2e")
   198  
   199  	_, _, _ = mkimage(t, imgName)
   200  
   201  	_, privKeyPath, pubKeyPath := importKeyPair(t, td)
   202  
   203  	ctx := context.Background()
   204  
   205  	// Now sign the image
   206  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   207  	so := options.SignOptions{
   208  		Upload: true,
   209  	}
   210  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   211  
   212  	// Now verify and download should work!
   213  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   214  	must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   215  
   216  	// Now clean signature from the given image
   217  	must(cli.CleanCmd(ctx, options.RegistryOptions{}, "all", imgName, true), t)
   218  
   219  	// It doesn't work
   220  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
   221  }
   222  
   223  func TestAttestVerify(t *testing.T) {
   224  	attestVerify(t,
   225  		"slsaprovenance",
   226  		`{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`,
   227  		`predicate: builder: id: "2"`,
   228  		`predicate: builder: id: "1"`,
   229  	)
   230  }
   231  
   232  func TestAttestVerifySPDXJSON(t *testing.T) {
   233  	attestationBytes, err := os.ReadFile("./testdata/bom-go-mod.spdx.json")
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	attestVerify(t,
   238  		"spdxjson",
   239  		string(attestationBytes),
   240  		`predicate: Data: spdxVersion: "SPDX-2.2"`,
   241  		`predicate: Data: spdxVersion: "SPDX-9.9"`,
   242  	)
   243  }
   244  
   245  func TestAttestVerifyCycloneDXJSON(t *testing.T) {
   246  	attestationBytes, err := os.ReadFile("./testdata/bom-go-mod.cyclonedx.json")
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	attestVerify(t,
   251  		"cyclonedx",
   252  		string(attestationBytes),
   253  		`predicate: Data: specVersion: "1.4"`,
   254  		`predicate: Data: specVersion: "7.7"`,
   255  	)
   256  }
   257  
   258  func TestAttestVerifyURI(t *testing.T) {
   259  	attestationBytes, err := os.ReadFile("./testdata/test-result.json")
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  	attestVerify(t,
   264  		"https://example.com/TestResult/v1",
   265  		string(attestationBytes),
   266  		`predicate: passed: true`,
   267  		`predicate: passed: false"`,
   268  	)
   269  }
   270  
   271  func attestVerify(t *testing.T, predicateType, attestation, goodCue, badCue string) {
   272  	repo, stop := reg(t)
   273  	defer stop()
   274  	td := t.TempDir()
   275  
   276  	var imgName, attestationPath string
   277  	if _, err := url.ParseRequestURI(predicateType); err == nil {
   278  		// If the predicate type is URI, it cannot be included as image name and path.
   279  		imgName = path.Join(repo, "cosign-attest-uri-e2e-image")
   280  		attestationPath = filepath.Join(td, "cosign-attest-uri-e2e-attestation")
   281  	} else {
   282  		imgName = path.Join(repo, fmt.Sprintf("cosign-attest-%s-e2e-image", predicateType))
   283  		attestationPath = filepath.Join(td, fmt.Sprintf("cosign-attest-%s-e2e-attestation", predicateType))
   284  	}
   285  
   286  	_, _, cleanup := mkimage(t, imgName)
   287  	defer cleanup()
   288  
   289  	_, privKeyPath, pubKeyPath := keypair(t, td)
   290  
   291  	ctx := context.Background()
   292  
   293  	// Verify should fail at first
   294  	verifyAttestation := cliverify.VerifyAttestationCommand{
   295  		KeyRef: pubKeyPath,
   296  	}
   297  
   298  	// Fail case when using without type and policy flag
   299  	mustErr(verifyAttestation.Exec(ctx, []string{imgName}), t)
   300  
   301  	if err := os.WriteFile(attestationPath, []byte(attestation), 0600); err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	// Now attest the image
   306  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   307  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, attestationPath, false,
   308  		predicateType, false, 30*time.Second, false), t)
   309  
   310  	// Use cue to verify attestation
   311  	policyPath := filepath.Join(td, "policy.cue")
   312  	verifyAttestation.PredicateType = predicateType
   313  	verifyAttestation.Policies = []string{policyPath}
   314  
   315  	// Fail case
   316  	if err := os.WriteFile(policyPath, []byte(badCue), 0600); err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	mustErr(verifyAttestation.Exec(ctx, []string{imgName}), t)
   320  
   321  	// Success case
   322  	if err := os.WriteFile(policyPath, []byte(goodCue), 0600); err != nil {
   323  		t.Fatal(err)
   324  	}
   325  	must(verifyAttestation.Exec(ctx, []string{imgName}), t)
   326  
   327  	// Look for a specific annotation
   328  	mustErr(verify(pubKeyPath, imgName, true, map[string]interface{}{"foo": "bar"}, ""), t)
   329  }
   330  
   331  func TestAttestationReplaceCreate(t *testing.T) {
   332  	repo, stop := reg(t)
   333  	defer stop()
   334  	td := t.TempDir()
   335  
   336  	imgName := path.Join(repo, "cosign-attest-replace-e2e")
   337  
   338  	_, _, cleanup := mkimage(t, imgName)
   339  	defer cleanup()
   340  
   341  	_, privKeyPath, _ := keypair(t, td)
   342  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   343  
   344  	ctx := context.Background()
   345  
   346  	slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
   347  	slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
   348  	if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
   349  		t.Fatal(err)
   350  	}
   351  
   352  	ref, err := name.ParseReference(imgName)
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  	regOpts := options.RegistryOptions{}
   357  	ociremoteOpts, err := regOpts.ClientOpts(ctx)
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  
   362  	// Attest with replace=true to create an attestation
   363  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false,
   364  		"slsaprovenance", true, 30*time.Second, false), t)
   365  
   366  	// Download and count the attestations
   367  	attestations, err := cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
   368  	if err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	if len(attestations) != 1 {
   372  		t.Fatal(fmt.Errorf("expected len(attestations) == 1, got %d", len(attestations)))
   373  	}
   374  }
   375  
   376  func TestExcessiveAttestations(t *testing.T) {
   377  	// skipping tst it is falky and taking too long
   378  	t.Skip()
   379  	repo, stop := reg(t)
   380  	defer stop()
   381  	td := t.TempDir()
   382  
   383  	imgName := path.Join(repo, "cosign-attest-download-e2e")
   384  
   385  	_, _, cleanup := mkimage(t, imgName)
   386  	defer cleanup()
   387  
   388  	_, privKeyPath, _ := keypair(t, td)
   389  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   390  
   391  	ctx := context.Background()
   392  
   393  	slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
   394  	slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
   395  	if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
   396  		t.Fatal(err)
   397  	}
   398  
   399  	vulnAttestation := `
   400  	{
   401      "invocation": {
   402        "parameters": null,
   403        "uri": "invocation.example.com/cosign-testing",
   404        "event_id": "",
   405        "builder.id": ""
   406      },
   407      "scanner": {
   408        "uri": "fakescanner.example.com/cosign-testing",
   409        "version": "",
   410        "db": {
   411          "uri": "",
   412          "version": ""
   413        },
   414        "result": null
   415      },
   416      "metadata": {
   417        "scanStartedOn": "2022-04-12T00:00:00Z",
   418        "scanFinishedOn": "2022-04-12T00:10:00Z"
   419      }
   420  }
   421  `
   422  	ref, err := name.ParseReference(imgName)
   423  	if err != nil {
   424  		t.Fatal(err)
   425  	}
   426  	regOpts := options.RegistryOptions{}
   427  	ociremoteOpts, err := regOpts.ClientOpts(ctx)
   428  	if err != nil {
   429  		t.Fatal(err)
   430  	}
   431  
   432  	for i := 0; i < 102; i++ {
   433  		vulnAttestationPath := filepath.Join(td, fmt.Sprintf("attestation-%d.vuln.json", i))
   434  		if err := os.WriteFile(vulnAttestationPath, []byte(vulnAttestation), 0600); err != nil {
   435  			t.Fatal(err)
   436  		}
   437  
   438  		// Attest to create a vuln attestation
   439  		must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, vulnAttestationPath, false,
   440  			"vuln", false, 30*time.Second, false), t)
   441  	}
   442  
   443  	_, err = cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
   444  	if err == nil {
   445  		t.Fatalf("Expected an error, but 'err' was 'nil'")
   446  	}
   447  	expectedError := "maximum number of attestations on an image is 100, found 102"
   448  	if err.Error() != expectedError {
   449  		t.Errorf("Exted the error to be: '%s' but it was '%s'", expectedError, err.Error())
   450  	}
   451  }
   452  
   453  func TestAttestationReplace(t *testing.T) {
   454  	repo, stop := reg(t)
   455  	defer stop()
   456  	td := t.TempDir()
   457  
   458  	imgName := path.Join(repo, "cosign-attest-replace-e2e")
   459  
   460  	_, _, cleanup := mkimage(t, imgName)
   461  	defer cleanup()
   462  
   463  	_, privKeyPath, _ := keypair(t, td)
   464  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   465  
   466  	ctx := context.Background()
   467  
   468  	slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
   469  	slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
   470  	if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
   471  		t.Fatal(err)
   472  	}
   473  
   474  	ref, err := name.ParseReference(imgName)
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  	regOpts := options.RegistryOptions{}
   479  	ociremoteOpts, err := regOpts.ClientOpts(ctx)
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  
   484  	// Attest once with replace=false creating an attestation
   485  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false,
   486  		"slsaprovenance", false, 30*time.Second, false), t)
   487  
   488  	// Download and count the attestations
   489  	attestations, err := cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
   490  	if err != nil {
   491  		t.Fatal(err)
   492  	}
   493  	if len(attestations) != 1 {
   494  		t.Fatal(fmt.Errorf("expected len(attestations) == 1, got %d", len(attestations)))
   495  	}
   496  
   497  	// Attest again with replace=true, replacing the previous attestation
   498  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false,
   499  		"slsaprovenance", true, 30*time.Second, false), t)
   500  	attestations, err = cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
   501  
   502  	// Download and count the attestations
   503  	if err != nil {
   504  		t.Fatal(err)
   505  	}
   506  	if len(attestations) != 1 {
   507  		t.Fatal(fmt.Errorf("expected len(attestations) == 1, got %d", len(attestations)))
   508  	}
   509  
   510  	// Attest once more replace=true using a different predicate, to ensure it adds a new attestation
   511  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false,
   512  		"custom", true, 30*time.Second, false), t)
   513  
   514  	// Download and count the attestations
   515  	attestations, err = cosign.FetchAttestationsForReference(ctx, ref, ociremoteOpts...)
   516  	if err != nil {
   517  		t.Fatal(err)
   518  	}
   519  	if len(attestations) != 2 {
   520  		t.Fatal(fmt.Errorf("expected len(attestations) == 2, got %d", len(attestations)))
   521  	}
   522  }
   523  
   524  func TestRekorBundle(t *testing.T) {
   525  	// turn on the tlog
   526  	defer setenv(t, env.VariableExperimental.String(), "1")()
   527  
   528  	repo, stop := reg(t)
   529  	defer stop()
   530  	td := t.TempDir()
   531  
   532  	imgName := path.Join(repo, "cosign-e2e")
   533  
   534  	_, _, cleanup := mkimage(t, imgName)
   535  	defer cleanup()
   536  
   537  	_, privKeyPath, pubKeyPath := keypair(t, td)
   538  
   539  	ko := options.KeyOpts{
   540  		KeyRef:   privKeyPath,
   541  		PassFunc: passFunc,
   542  		RekorURL: rekorURL,
   543  	}
   544  	so := options.SignOptions{
   545  		Upload: true,
   546  	}
   547  
   548  	// Sign the image
   549  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   550  	// Make sure verify works
   551  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   552  
   553  	// Make sure offline verification works with bundling
   554  	// use rekor prod since we have hardcoded the public key
   555  	os.Setenv(serverEnv, "notreal")
   556  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   557  }
   558  
   559  func TestDuplicateSign(t *testing.T) {
   560  	repo, stop := reg(t)
   561  	defer stop()
   562  	td := t.TempDir()
   563  
   564  	imgName := path.Join(repo, "cosign-e2e")
   565  
   566  	ref, _, cleanup := mkimage(t, imgName)
   567  	defer cleanup()
   568  
   569  	_, privKeyPath, pubKeyPath := keypair(t, td)
   570  
   571  	ctx := context.Background()
   572  	// Verify should fail at first
   573  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
   574  	// So should download
   575  	mustErr(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   576  
   577  	// Now sign the image
   578  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   579  	so := options.SignOptions{
   580  		Upload: true,
   581  	}
   582  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   583  
   584  	// Now verify and download should work!
   585  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
   586  	must(download.SignatureCmd(ctx, options.RegistryOptions{}, imgName), t)
   587  
   588  	// Signing again should work just fine...
   589  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   590  
   591  	se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...))
   592  	must(err, t)
   593  	sigs, err := se.Signatures()
   594  	must(err, t)
   595  	signatures, err := sigs.Get()
   596  	must(err, t)
   597  
   598  	if len(signatures) > 1 {
   599  		t.Errorf("expected there to only be one signature, got %v", signatures)
   600  	}
   601  }
   602  
   603  func TestExcessiveSignatures(t *testing.T) {
   604  	repo, stop := reg(t)
   605  	defer stop()
   606  	td := t.TempDir()
   607  
   608  	imgName := path.Join(repo, "cosign-e2e")
   609  
   610  	_, _, cleanup := mkimage(t, imgName)
   611  	defer cleanup()
   612  
   613  	ctx := context.Background()
   614  
   615  	for i := 0; i < 102; i++ {
   616  		_, privKeyPath, _ := keypair(t, td)
   617  
   618  		// Sign the image
   619  		ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
   620  		so := options.SignOptions{
   621  			Upload: true,
   622  		}
   623  		must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   624  	}
   625  	err := download.SignatureCmd(ctx, options.RegistryOptions{}, imgName)
   626  	if err == nil {
   627  		t.Fatal("Expected an error, but 'err' was 'nil'")
   628  	}
   629  	expectedErr := "maximum number of signatures on an image is 100, found 102"
   630  	if err.Error() != expectedErr {
   631  		t.Fatalf("Expected the error '%s', but got the error '%s'", expectedErr, err.Error())
   632  	}
   633  }
   634  
   635  func TestKeyURLVerify(t *testing.T) {
   636  	// TODO: re-enable once distroless images are being signed by the new client
   637  	t.Skip()
   638  	// Verify that an image can be verified via key url
   639  	keyRef := "https://raw.githubusercontent.com/GoogleContainerTools/distroless/main/cosign.pub"
   640  	img := "gcr.io/distroless/base:latest"
   641  
   642  	must(verify(keyRef, img, true, nil, ""), t)
   643  }
   644  
   645  func TestGenerateKeyPairEnvVar(t *testing.T) {
   646  	defer setenv(t, "COSIGN_PASSWORD", "foo")()
   647  	keys, err := cosign.GenerateKeyPair(generate.GetPass)
   648  	if err != nil {
   649  		t.Fatal(err)
   650  	}
   651  	if _, err := cosign.LoadPrivateKey(keys.PrivateBytes, []byte("foo")); err != nil {
   652  		t.Fatal(err)
   653  	}
   654  }
   655  
   656  func TestGenerateKeyPairK8s(t *testing.T) {
   657  	td := t.TempDir()
   658  	wd, err := os.Getwd()
   659  	if err != nil {
   660  		t.Fatal(err)
   661  	}
   662  	if err := os.Chdir(td); err != nil {
   663  		t.Fatal(err)
   664  	}
   665  	defer func() {
   666  		os.Chdir(wd)
   667  	}()
   668  	password := "foo"
   669  	defer setenv(t, "COSIGN_PASSWORD", password)()
   670  	ctx := context.Background()
   671  	name := "cosign-secret"
   672  	namespace := "default"
   673  	if err := kubernetes.KeyPairSecret(ctx, fmt.Sprintf("k8s://%s/%s", namespace, name), generate.GetPass); err != nil {
   674  		t.Fatal(err)
   675  	}
   676  	// make sure the secret actually exists
   677  
   678  	cfg, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
   679  		clientcmd.NewDefaultClientConfigLoadingRules(), nil).ClientConfig()
   680  	if err != nil {
   681  		t.Fatal(err)
   682  	}
   683  	client, err := k8s.NewForConfig(cfg)
   684  	if err != nil {
   685  		t.Fatal(err)
   686  	}
   687  	s, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
   688  	if err != nil {
   689  		t.Fatal(err)
   690  	}
   691  	if v, ok := s.Data["cosign.password"]; !ok || string(v) != password {
   692  		t.Fatalf("password is incorrect, got %v expected %v", v, "foo")
   693  	}
   694  }
   695  
   696  func TestMultipleSignatures(t *testing.T) {
   697  	repo, stop := reg(t)
   698  	defer stop()
   699  
   700  	td1 := t.TempDir()
   701  	td2 := t.TempDir()
   702  
   703  	imgName := path.Join(repo, "cosign-e2e")
   704  
   705  	_, _, cleanup := mkimage(t, imgName)
   706  	defer cleanup()
   707  
   708  	_, priv1, pub1 := keypair(t, td1)
   709  	_, priv2, pub2 := keypair(t, td2)
   710  
   711  	// Verify should fail at first for both keys
   712  	mustErr(verify(pub1, imgName, true, nil, ""), t)
   713  	mustErr(verify(pub2, imgName, true, nil, ""), t)
   714  
   715  	// Now sign the image with one key
   716  	ko := options.KeyOpts{KeyRef: priv1, PassFunc: passFunc}
   717  	so := options.SignOptions{
   718  		Upload: true,
   719  	}
   720  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   721  	// Now verify should work with that one, but not the other
   722  	must(verify(pub1, imgName, true, nil, ""), t)
   723  	mustErr(verify(pub2, imgName, true, nil, ""), t)
   724  
   725  	// Now sign with the other key too
   726  	ko.KeyRef = priv2
   727  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
   728  
   729  	// Now verify should work with both
   730  	must(verify(pub1, imgName, true, nil, ""), t)
   731  	must(verify(pub2, imgName, true, nil, ""), t)
   732  }
   733  
   734  func TestSignBlob(t *testing.T) {
   735  	blob := "someblob"
   736  	td1 := t.TempDir()
   737  	td2 := t.TempDir()
   738  	t.Cleanup(func() {
   739  		os.RemoveAll(td1)
   740  		os.RemoveAll(td2)
   741  	})
   742  	bp := filepath.Join(td1, blob)
   743  
   744  	if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
   745  		t.Fatal(err)
   746  	}
   747  
   748  	_, privKeyPath1, pubKeyPath1 := keypair(t, td1)
   749  	_, _, pubKeyPath2 := keypair(t, td2)
   750  
   751  	ctx := context.Background()
   752  
   753  	ko1 := options.KeyOpts{
   754  		KeyRef: pubKeyPath1,
   755  	}
   756  	ko2 := options.KeyOpts{
   757  		KeyRef: pubKeyPath2,
   758  	}
   759  	// Verify should fail on a bad input
   760  	mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   761  	mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "badsig", blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   762  
   763  	// Now sign the blob with one key
   764  	ko := options.KeyOpts{
   765  		KeyRef:   privKeyPath1,
   766  		PassFunc: passFunc,
   767  	}
   768  	sig, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", "")
   769  	if err != nil {
   770  		t.Fatal(err)
   771  	}
   772  	// Now verify should work with that one, but not the other
   773  	must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   774  	mustErr(cliverify.VerifyBlobCmd(ctx, ko2, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, string(sig), bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   775  }
   776  
   777  func TestSignBlobBundle(t *testing.T) {
   778  	blob := "someblob"
   779  	td1 := t.TempDir()
   780  	t.Cleanup(func() {
   781  		os.RemoveAll(td1)
   782  	})
   783  	bp := filepath.Join(td1, blob)
   784  	bundlePath := filepath.Join(td1, "bundle.sig")
   785  
   786  	if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
   787  		t.Fatal(err)
   788  	}
   789  
   790  	_, privKeyPath1, pubKeyPath1 := keypair(t, td1)
   791  
   792  	ctx := context.Background()
   793  
   794  	ko1 := options.KeyOpts{
   795  		KeyRef:     pubKeyPath1,
   796  		BundlePath: bundlePath,
   797  	}
   798  	// Verify should fail on a bad input
   799  	mustErr(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, blob, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   800  
   801  	// Now sign the blob with one key
   802  	ko := options.KeyOpts{
   803  		KeyRef:     privKeyPath1,
   804  		PassFunc:   passFunc,
   805  		BundlePath: bundlePath,
   806  		RekorURL:   rekorURL,
   807  	}
   808  	if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
   809  		t.Fatal(err)
   810  	}
   811  	// Now verify should work
   812  	must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   813  
   814  	// Now we turn on the tlog and sign again
   815  	defer setenv(t, env.VariableExperimental.String(), "1")()
   816  	if _, err := sign.SignBlobCmd(ro, ko, options.RegistryOptions{}, bp, true, "", ""); err != nil {
   817  		t.Fatal(err)
   818  	}
   819  
   820  	// Point to a fake rekor server to make sure offline verification of the tlog entry works
   821  	os.Setenv(serverEnv, "notreal")
   822  	must(cliverify.VerifyBlobCmd(ctx, ko1, "" /*certRef*/, "" /*certEmail*/, "" /*certIdentity*/, "" /*certOidcIssuer*/, "" /*certChain*/, "" /*sigRef*/, bp, "" /*certGithubWorkflowTrigger*/, "" /*certGithubWorkflowSha*/, "" /*certGithubWorkflowName*/, "" /*certGithubWorkflowRepository*/, "" /*certGithubWorkflowRef*/, false), t)
   823  }
   824  
   825  func TestGenerate(t *testing.T) {
   826  	repo, stop := reg(t)
   827  	defer stop()
   828  
   829  	imgName := path.Join(repo, "cosign-e2e")
   830  	_, desc, cleanup := mkimage(t, imgName)
   831  	defer cleanup()
   832  
   833  	// Generate the payload for the image, and check the digest.
   834  	b := bytes.Buffer{}
   835  	must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, nil, &b), t)
   836  	ss := payload.SimpleContainerImage{}
   837  	must(json.Unmarshal(b.Bytes(), &ss), t)
   838  
   839  	equals(desc.Digest.String(), ss.Critical.Image.DockerManifestDigest, t)
   840  
   841  	// Now try with some annotations.
   842  	b.Reset()
   843  	a := map[string]interface{}{"foo": "bar"}
   844  	must(generate.GenerateCmd(context.Background(), options.RegistryOptions{}, imgName, a, &b), t)
   845  	must(json.Unmarshal(b.Bytes(), &ss), t)
   846  
   847  	equals(desc.Digest.String(), ss.Critical.Image.DockerManifestDigest, t)
   848  	equals(ss.Optional["foo"], "bar", t)
   849  }
   850  
   851  func keypair(t *testing.T, td string) (*cosign.KeysBytes, string, string) {
   852  	wd, err := os.Getwd()
   853  	if err != nil {
   854  		t.Fatal(err)
   855  	}
   856  	if err := os.Chdir(td); err != nil {
   857  		t.Fatal(err)
   858  	}
   859  	defer func() {
   860  		os.Chdir(wd)
   861  	}()
   862  	keys, err := cosign.GenerateKeyPair(passFunc)
   863  	if err != nil {
   864  		t.Fatal(err)
   865  	}
   866  
   867  	privKeyPath := filepath.Join(td, "cosign.key")
   868  	if err := os.WriteFile(privKeyPath, keys.PrivateBytes, 0600); err != nil {
   869  		t.Fatal(err)
   870  	}
   871  
   872  	pubKeyPath := filepath.Join(td, "cosign.pub")
   873  	if err := os.WriteFile(pubKeyPath, keys.PublicBytes, 0600); err != nil {
   874  		t.Fatal(err)
   875  	}
   876  	return keys, privKeyPath, pubKeyPath
   877  }
   878  
   879  func importKeyPair(t *testing.T, td string) (*cosign.KeysBytes, string, string) {
   880  
   881  	const validrsa1 = `-----BEGIN RSA PRIVATE KEY-----
   882  MIIEogIBAAKCAQEAx5piWVlE62NnZ0UzJ8Z6oKiKOC4dbOZ1HsNhIRtqkM+Oq4G+
   883  25yq6P+0JU/Qvr9veOGEb3R/J9u8JBo+hv2i5X8OtgvP2V2pi6f1s6vK7L0+6uRb
   884  4YTT/UdMshaVf97MgEqbq41Jf/cuvh+3AV0tZ1BpixZg4aXMKpY6HUP69lbsu27o
   885  SUN1myMv7TSgZiV4CYs3l/gkEfpysBptWlcHRuw5RsB+C0RbjRtbJ/5VxmE/vd3M
   886  lafd5t1WSpMb8yf0a84u5NFaXwZ7CweMfXeOddS0yb19ShSuW3PPRadruBM1mq15
   887  js9GfagPxDS75Imcs+fA62lWvHxEujTGjYHxawIDAQABAoIBAH+sgLwmHa9zJfEo
   888  klAe5NFe/QpydN/ziXbkAnzqzH9URC3wD+TpkWj4JoK3Sw635NWtasjf+3XDV9S/
   889  9L7j/g5N91r6sziWcJykEsWaXXKQmm4lI6BdFjwsHyLKz1W7bZOiJXDWLu1rbrqu
   890  DqEQuLoc9WXCKrYrFy0maoXNtfla/1p05kKN0bMigcnnyAQ+xBTwoyco4tkIz5se
   891  IYxorz7qzXrkHQI+knz5BawmNe3ekoSaXUPoLoOR7TRTGsLteL5yukvWAi8S/0rE
   892  gftC+PZCQpoQhSUYq7wXe7RowJ1f+kXb7HsSedOTfTSW1D/pUb/uW+CcRKig42ZI
   893  I9H9TAECgYEA5XGBML6fJyWVqx64sHbUAjQsmQ0RwU6Zo7sqHIEPf6tYVYp7KtzK
   894  KOfi8seOOL5FSy4pjCo11Dzyrh9bn45RNmtjSYTgOnVPSoCfuRNfOcpG+/wCHjYf
   895  EjDvdrCpbg59kVUeaMeBDiyWAlM48HJAn8O7ez2U/iKQCyJmOIwFhSkCgYEA3rSz
   896  Fi1NzqYWxWos4NBmg8iKcQ9SMkmPdgRLAs/WNnZJ8fdgJZwihevkXGytRGJEmav2
   897  GMKRx1g6ey8fjXTQH9WM8X/kJC5fv8wLHnUCH/K3Mcp9CYwn7PFvSnBr4kQoc/el
   898  bURhcF1+/opEC8vNX/Wk3zAG7Xs1PREXlH2SIHMCgYBV/3kgwBH/JkM25EjtO1yz
   899  hsLAivmAruk/SUO7c1RP0fVF+qW3pxHOyztxLALOmeJ3D1JbSubqKf377Zz17O3b
   900  q9yHDdrNjnKtxhAX2n7ytjJs+EQC9t4mf1kB761RpvTBqFnBhCWHHocLUA4jcW9v
   901  cnmu86IIrwO2aKpPv4vCIQKBgHU9gY3qOazRSOmSlJ+hdmZn+2G7pBTvHsQNTIPl
   902  cCrpqNHl3crO4GnKHkT9vVVjuiOAIKU2QNJFwzu4Og8Y8LvhizpTjoHxm9x3iV72
   903  UDELcJ+YrqyJCTe2flUcy96o7Pbn50GXnwgtYD6WAW6IUszyn2ITgYIhu4wzZEt6
   904  s6O7AoGAPTKbRA87L34LMlXyUBJma+etMARIP1zu8bXJ7hSJeMcog8zaLczN7ruT
   905  pGAaLxggvtvuncMuTrG+cdmsR9SafSFKRS92NCxhOUonQ+NP6mLskIGzJZoQ5JvQ
   906  qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA=
   907  -----END RSA PRIVATE KEY-----`
   908  
   909  	wd, err := os.Getwd()
   910  	if err != nil {
   911  		t.Fatal(err)
   912  	}
   913  	if err := os.Chdir(td); err != nil {
   914  		t.Fatal(err)
   915  	}
   916  	defer func() {
   917  		os.Chdir(wd)
   918  	}()
   919  
   920  	err = os.WriteFile("validrsa1.key", []byte(validrsa1), 0600)
   921  	if err != nil {
   922  		t.Fatal(err)
   923  	}
   924  
   925  	keys, err := cosign.ImportKeyPair("validrsa1.key", passFunc)
   926  	if err != nil {
   927  		t.Fatal(err)
   928  	}
   929  
   930  	privKeyPath := filepath.Join(td, "import-cosign.key")
   931  	if err := os.WriteFile(privKeyPath, keys.PrivateBytes, 0600); err != nil {
   932  		t.Fatal(err)
   933  	}
   934  
   935  	pubKeyPath := filepath.Join(td, "import-cosign.pub")
   936  	if err := os.WriteFile(pubKeyPath, keys.PublicBytes, 0600); err != nil {
   937  		t.Fatal(err)
   938  	}
   939  	return keys, privKeyPath, pubKeyPath
   940  
   941  }
   942  
   943  func TestUploadDownload(t *testing.T) {
   944  	repo, stop := reg(t)
   945  	defer stop()
   946  	td := t.TempDir()
   947  	ctx := context.Background()
   948  
   949  	testCases := map[string]struct {
   950  		signature     string
   951  		signatureType attach.SignatureArgType
   952  		expectedErr   bool
   953  	}{
   954  		"file containing signature": {
   955  			signature:     "testsignaturefile",
   956  			signatureType: attach.FileSignature,
   957  			expectedErr:   false,
   958  		},
   959  		"raw signature as argument": {
   960  			signature:     "testsignatureraw",
   961  			signatureType: attach.RawSignature,
   962  			expectedErr:   false,
   963  		},
   964  		"empty signature as argument": {
   965  			signature:     "",
   966  			signatureType: attach.RawSignature,
   967  			expectedErr:   true,
   968  		},
   969  	}
   970  
   971  	imgName := path.Join(repo, "cosign-e2e")
   972  	for testName, testCase := range testCases {
   973  		t.Run(testName, func(t *testing.T) {
   974  			ref, _, cleanup := mkimage(t, imgName)
   975  			payload := "testpayload"
   976  			payloadPath := mkfile(payload, td, t)
   977  			signature := base64.StdEncoding.EncodeToString([]byte(testCase.signature))
   978  
   979  			var sigRef string
   980  			if testCase.signatureType == attach.FileSignature {
   981  				sigRef = mkfile(signature, td, t)
   982  			} else {
   983  				sigRef = signature
   984  			}
   985  
   986  			// Upload it!
   987  			err := attach.SignatureCmd(ctx, options.RegistryOptions{}, sigRef, payloadPath, imgName)
   988  			if testCase.expectedErr {
   989  				mustErr(err, t)
   990  			} else {
   991  				must(err, t)
   992  			}
   993  
   994  			// Now download it!
   995  			se, err := ociremote.SignedEntity(ref, ociremote.WithRemoteOptions(registryClientOpts(ctx)...))
   996  			must(err, t)
   997  			sigs, err := se.Signatures()
   998  			must(err, t)
   999  			signatures, err := sigs.Get()
  1000  			must(err, t)
  1001  
  1002  			if testCase.expectedErr {
  1003  				if len(signatures) != 0 {
  1004  					t.Fatalf("unexpected signatures %d, wanted 0", len(signatures))
  1005  				}
  1006  			} else {
  1007  				if len(signatures) != 1 {
  1008  					t.Fatalf("unexpected signatures %d, wanted 1", len(signatures))
  1009  				}
  1010  
  1011  				if b64sig, err := signatures[0].Base64Signature(); err != nil {
  1012  					t.Fatalf("Base64Signature() = %v", err)
  1013  				} else if diff := cmp.Diff(b64sig, signature); diff != "" {
  1014  					t.Error(diff)
  1015  				}
  1016  
  1017  				if p, err := signatures[0].Payload(); err != nil {
  1018  					t.Fatalf("Payload() = %v", err)
  1019  				} else if diff := cmp.Diff(p, []byte(payload)); diff != "" {
  1020  					t.Error(diff)
  1021  				}
  1022  			}
  1023  
  1024  			// Now delete it!
  1025  			cleanup()
  1026  		})
  1027  	}
  1028  }
  1029  
  1030  func TestUploadBlob(t *testing.T) {
  1031  	repo, stop := reg(t)
  1032  	defer stop()
  1033  	td := t.TempDir()
  1034  	ctx := context.Background()
  1035  
  1036  	imgName := path.Join(repo, "/cosign-upload-e2e")
  1037  	payload := "testpayload"
  1038  	payloadPath := mkfile(payload, td, t)
  1039  
  1040  	// Upload it!
  1041  	files := []cremote.File{cremote.FileFromFlag(payloadPath)}
  1042  	must(upload.BlobCmd(ctx, options.RegistryOptions{}, files, nil, "", imgName), t)
  1043  
  1044  	// Check it
  1045  	ref, err := name.ParseReference(imgName)
  1046  	if err != nil {
  1047  		t.Fatal(err)
  1048  	}
  1049  
  1050  	// Now download it with sget (this should fail by tag)
  1051  	if err := sget.New(imgName, "", os.Stdout).Do(ctx); err == nil {
  1052  		t.Error("expected download to fail")
  1053  	}
  1054  
  1055  	img, err := remote.Image(ref)
  1056  	if err != nil {
  1057  		t.Fatal(err)
  1058  	}
  1059  	dgst, err := img.Digest()
  1060  	if err != nil {
  1061  		t.Fatal(err)
  1062  	}
  1063  
  1064  	result := &bytes.Buffer{}
  1065  
  1066  	// But pass by digest
  1067  	if err := sget.New(imgName+"@"+dgst.String(), "", result).Do(ctx); err != nil {
  1068  		t.Fatal(err)
  1069  	}
  1070  	b, err := io.ReadAll(result)
  1071  	if err != nil {
  1072  		t.Fatal(err)
  1073  	}
  1074  	if string(b) != payload {
  1075  		t.Errorf("expected contents to be %s, got %s", payload, string(b))
  1076  	}
  1077  }
  1078  
  1079  func TestSaveLoad(t *testing.T) {
  1080  	tests := []struct {
  1081  		description     string
  1082  		getSignedEntity func(t *testing.T, n string) (name.Reference, *remote.Descriptor, func())
  1083  	}{
  1084  		{
  1085  			description:     "save and load an image",
  1086  			getSignedEntity: mkimage,
  1087  		},
  1088  		{
  1089  			description:     "save and load an image index",
  1090  			getSignedEntity: mkimageindex,
  1091  		},
  1092  	}
  1093  	for i, test := range tests {
  1094  		t.Run(test.description, func(t *testing.T) {
  1095  			repo, stop := reg(t)
  1096  			defer stop()
  1097  			keysDir := t.TempDir()
  1098  
  1099  			imgName := path.Join(repo, fmt.Sprintf("save-load-%d", i))
  1100  
  1101  			_, _, cleanup := test.getSignedEntity(t, imgName)
  1102  			defer cleanup()
  1103  
  1104  			_, privKeyPath, pubKeyPath := keypair(t, keysDir)
  1105  
  1106  			ctx := context.Background()
  1107  			// Now sign the image and verify it
  1108  			ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
  1109  			so := options.SignOptions{
  1110  				Upload: true,
  1111  			}
  1112  			must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1113  			must(verify(pubKeyPath, imgName, true, nil, ""), t)
  1114  
  1115  			// save the image to a temp dir
  1116  			imageDir := t.TempDir()
  1117  			must(cli.SaveCmd(ctx, options.SaveOptions{Directory: imageDir}, imgName), t)
  1118  
  1119  			// verify the local image using a local key
  1120  			must(verifyLocal(pubKeyPath, imageDir, true, nil, ""), t)
  1121  
  1122  			// load the image from the temp dir into a new image and verify the new image
  1123  			imgName2 := path.Join(repo, fmt.Sprintf("save-load-%d-2", i))
  1124  			must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir}, imgName2), t)
  1125  			must(verify(pubKeyPath, imgName2, true, nil, ""), t)
  1126  		})
  1127  	}
  1128  }
  1129  
  1130  func TestSaveLoadAttestation(t *testing.T) {
  1131  	repo, stop := reg(t)
  1132  	defer stop()
  1133  	td := t.TempDir()
  1134  
  1135  	imgName := path.Join(repo, "save-load")
  1136  
  1137  	_, _, cleanup := mkimage(t, imgName)
  1138  	defer cleanup()
  1139  
  1140  	_, privKeyPath, pubKeyPath := keypair(t, td)
  1141  
  1142  	ctx := context.Background()
  1143  	// Now sign the image and verify it
  1144  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
  1145  	so := options.SignOptions{
  1146  		Upload: true,
  1147  	}
  1148  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1149  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
  1150  
  1151  	// now, append an attestation to the image
  1152  	slsaAttestation := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
  1153  	slsaAttestationPath := filepath.Join(td, "attestation.slsa.json")
  1154  	if err := os.WriteFile(slsaAttestationPath, []byte(slsaAttestation), 0600); err != nil {
  1155  		t.Fatal(err)
  1156  	}
  1157  
  1158  	// Now attest the image
  1159  	ko = options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc}
  1160  	must(attest.AttestCmd(ctx, ko, options.RegistryOptions{}, imgName, "", "", false, slsaAttestationPath, false,
  1161  		"slsaprovenance", false, 30*time.Second, false), t)
  1162  
  1163  	// save the image to a temp dir
  1164  	imageDir := t.TempDir()
  1165  	must(cli.SaveCmd(ctx, options.SaveOptions{Directory: imageDir}, imgName), t)
  1166  
  1167  	// load the image from the temp dir into a new image and verify the new image
  1168  	imgName2 := path.Join(repo, "save-load-2")
  1169  	must(cli.LoadCmd(ctx, options.LoadOptions{Directory: imageDir}, imgName2), t)
  1170  	must(verify(pubKeyPath, imgName2, true, nil, ""), t)
  1171  	// Use cue to verify attestation on the new image
  1172  	policyPath := filepath.Join(td, "policy.cue")
  1173  	verifyAttestation := cliverify.VerifyAttestationCommand{
  1174  		KeyRef: pubKeyPath,
  1175  	}
  1176  	verifyAttestation.PredicateType = "slsaprovenance"
  1177  	verifyAttestation.Policies = []string{policyPath}
  1178  	// Success case (remote)
  1179  	cuePolicy := `predicate: builder: id: "2"`
  1180  	if err := os.WriteFile(policyPath, []byte(cuePolicy), 0600); err != nil {
  1181  		t.Fatal(err)
  1182  	}
  1183  	must(verifyAttestation.Exec(ctx, []string{imgName2}), t)
  1184  	// Success case (local)
  1185  	verifyAttestation.LocalImage = true
  1186  	must(verifyAttestation.Exec(ctx, []string{imageDir}), t)
  1187  }
  1188  
  1189  func TestAttachSBOM(t *testing.T) {
  1190  	repo, stop := reg(t)
  1191  	defer stop()
  1192  	ctx := context.Background()
  1193  
  1194  	imgName := path.Join(repo, "sbom-image")
  1195  	img, _, cleanup := mkimage(t, imgName)
  1196  	defer cleanup()
  1197  
  1198  	out := bytes.Buffer{}
  1199  
  1200  	_, errPl := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{Platform: "darwin/amd64"}, img.Name(), &out)
  1201  	if errPl == nil {
  1202  		t.Fatalf("Expected error when passing Platform to single arch image")
  1203  	}
  1204  	_, err := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{}, img.Name(), &out)
  1205  	if err == nil {
  1206  		t.Fatal("Expected error")
  1207  	}
  1208  	t.Log(out.String())
  1209  	out.Reset()
  1210  
  1211  	// Upload it!
  1212  	must(attach.SBOMCmd(ctx, options.RegistryOptions{}, "./testdata/bom-go-mod.spdx", "spdx", imgName), t)
  1213  
  1214  	sboms, err := download.SBOMCmd(ctx, options.RegistryOptions{}, options.SBOMDownloadOptions{}, imgName, &out)
  1215  	if err != nil {
  1216  		t.Fatal(err)
  1217  	}
  1218  	t.Log(out.String())
  1219  	if len(sboms) != 1 {
  1220  		t.Fatalf("Expected one sbom, got %d", len(sboms))
  1221  	}
  1222  	want, err := os.ReadFile("./testdata/bom-go-mod.spdx")
  1223  	if err != nil {
  1224  		t.Fatal(err)
  1225  	}
  1226  	if diff := cmp.Diff(string(want), sboms[0]); diff != "" {
  1227  		t.Errorf("diff: %s", diff)
  1228  	}
  1229  
  1230  	// Generate key pairs to sign the sbom
  1231  	td1 := t.TempDir()
  1232  	td2 := t.TempDir()
  1233  	_, privKeyPath1, pubKeyPath1 := keypair(t, td1)
  1234  	_, _, pubKeyPath2 := keypair(t, td2)
  1235  
  1236  	// Verify should fail on a bad input
  1237  	mustErr(verify(pubKeyPath1, imgName, true, nil, "sbom"), t)
  1238  	mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom"), t)
  1239  
  1240  	// Now sign the sbom with one key
  1241  	ko1 := options.KeyOpts{KeyRef: privKeyPath1, PassFunc: passFunc}
  1242  	so := options.SignOptions{
  1243  		Upload:     true,
  1244  		Attachment: "sbom",
  1245  	}
  1246  	must(sign.SignCmd(ro, ko1, so, []string{imgName}), t)
  1247  
  1248  	// Now verify should work with that one, but not the other
  1249  	must(verify(pubKeyPath1, imgName, true, nil, "sbom"), t)
  1250  	mustErr(verify(pubKeyPath2, imgName, true, nil, "sbom"), t)
  1251  }
  1252  
  1253  func setenv(t *testing.T, k, v string) func() {
  1254  	if err := os.Setenv(k, v); err != nil {
  1255  		t.Fatalf("error setting env: %v", err)
  1256  	}
  1257  	return func() {
  1258  		os.Unsetenv(k)
  1259  	}
  1260  }
  1261  
  1262  func TestTlog(t *testing.T) {
  1263  	repo, stop := reg(t)
  1264  	defer stop()
  1265  	td := t.TempDir()
  1266  
  1267  	imgName := path.Join(repo, "cosign-e2e")
  1268  
  1269  	_, _, cleanup := mkimage(t, imgName)
  1270  	defer cleanup()
  1271  
  1272  	_, privKeyPath, pubKeyPath := keypair(t, td)
  1273  
  1274  	// Verify should fail at first
  1275  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
  1276  
  1277  	// Now sign the image without the tlog
  1278  	ko := options.KeyOpts{
  1279  		KeyRef:   privKeyPath,
  1280  		PassFunc: passFunc,
  1281  		RekorURL: rekorURL,
  1282  	}
  1283  	so := options.SignOptions{
  1284  		Upload: true,
  1285  	}
  1286  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1287  
  1288  	// Now verify should work!
  1289  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
  1290  
  1291  	// Now we turn on the tlog!
  1292  	defer setenv(t, env.VariableExperimental.String(), "1")()
  1293  
  1294  	// Verify shouldn't work since we haven't put anything in it yet.
  1295  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
  1296  
  1297  	// Sign again with the tlog env var on
  1298  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1299  	// And now verify works!
  1300  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
  1301  }
  1302  
  1303  func TestNoTlog(t *testing.T) {
  1304  	repo, stop := reg(t)
  1305  	defer stop()
  1306  	td := t.TempDir()
  1307  
  1308  	imgName := path.Join(repo, "cosign-e2e")
  1309  
  1310  	_, _, cleanup := mkimage(t, imgName)
  1311  	defer cleanup()
  1312  
  1313  	_, privKeyPath, pubKeyPath := keypair(t, td)
  1314  
  1315  	// Verify should fail at first
  1316  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
  1317  
  1318  	// Now sign the image without the tlog
  1319  	ko := options.KeyOpts{
  1320  		KeyRef:   privKeyPath,
  1321  		PassFunc: passFunc,
  1322  		RekorURL: rekorURL,
  1323  	}
  1324  	so := options.SignOptions{
  1325  		Upload: true,
  1326  	}
  1327  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1328  
  1329  	// Now verify should work!
  1330  	must(verify(pubKeyPath, imgName, true, nil, ""), t)
  1331  
  1332  	// Now we turn on the tlog!
  1333  	defer setenv(t, env.VariableExperimental.String(), "1")()
  1334  
  1335  	// Verify shouldn't work since we haven't put anything in it yet.
  1336  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
  1337  
  1338  	// Sign again with the tlog env var on with option to not upload tlog
  1339  	so = options.SignOptions{
  1340  		NoTlogUpload: true,
  1341  	}
  1342  	must(sign.SignCmd(ro, ko, so, []string{imgName}), t)
  1343  	// And verify it still fails.
  1344  	mustErr(verify(pubKeyPath, imgName, true, nil, ""), t)
  1345  }
  1346  
  1347  func TestGetPublicKeyCustomOut(t *testing.T) {
  1348  	td := t.TempDir()
  1349  	keys, privKeyPath, _ := keypair(t, td)
  1350  	ctx := context.Background()
  1351  
  1352  	outFile := "output.pub"
  1353  	outPath := filepath.Join(td, outFile)
  1354  	outWriter, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE, 0600)
  1355  	must(err, t)
  1356  
  1357  	pk := publickey.Pkopts{
  1358  		KeyRef: privKeyPath,
  1359  	}
  1360  	must(publickey.GetPublicKey(ctx, pk, publickey.NamedWriter{Name: outPath, Writer: outWriter}, passFunc), t)
  1361  
  1362  	output, err := os.ReadFile(outPath)
  1363  	must(err, t)
  1364  	equals(keys.PublicBytes, output, t)
  1365  }
  1366  
  1367  func mkfile(contents, td string, t *testing.T) string {
  1368  	f, err := os.CreateTemp(td, "")
  1369  	if err != nil {
  1370  		t.Fatal(err)
  1371  	}
  1372  	defer f.Close()
  1373  	if _, err := f.Write([]byte(contents)); err != nil {
  1374  		t.Fatal(err)
  1375  	}
  1376  	return f.Name()
  1377  }
  1378  
  1379  func mkimage(t *testing.T, n string) (name.Reference, *remote.Descriptor, func()) {
  1380  	ref, err := name.ParseReference(n, name.WeakValidation)
  1381  	if err != nil {
  1382  		t.Fatal(err)
  1383  	}
  1384  	img, err := random.Image(512, 5)
  1385  	if err != nil {
  1386  		t.Fatal(err)
  1387  	}
  1388  
  1389  	regClientOpts := registryClientOpts(context.Background())
  1390  
  1391  	if err := remote.Write(ref, img, regClientOpts...); err != nil {
  1392  		t.Fatal(err)
  1393  	}
  1394  
  1395  	remoteImage, err := remote.Get(ref, regClientOpts...)
  1396  	if err != nil {
  1397  		t.Fatal(err)
  1398  	}
  1399  
  1400  	cleanup := func() {
  1401  		_ = remote.Delete(ref, regClientOpts...)
  1402  		ref, _ := ociremote.SignatureTag(ref.Context().Digest(remoteImage.Descriptor.Digest.String()), ociremote.WithRemoteOptions(regClientOpts...))
  1403  		_ = remote.Delete(ref, regClientOpts...)
  1404  	}
  1405  	return ref, remoteImage, cleanup
  1406  }
  1407  
  1408  func mkimageindex(t *testing.T, n string) (name.Reference, *remote.Descriptor, func()) {
  1409  	ref, err := name.ParseReference(n, name.WeakValidation)
  1410  	if err != nil {
  1411  		t.Fatal(err)
  1412  	}
  1413  	ii, err := random.Index(512, 5, 4)
  1414  	if err != nil {
  1415  		t.Fatal(err)
  1416  	}
  1417  
  1418  	regClientOpts := registryClientOpts(context.Background())
  1419  
  1420  	if err := remote.WriteIndex(ref, ii, regClientOpts...); err != nil {
  1421  		t.Fatal(err)
  1422  	}
  1423  
  1424  	remoteIndex, err := remote.Get(ref, regClientOpts...)
  1425  	if err != nil {
  1426  		t.Fatal(err)
  1427  	}
  1428  
  1429  	cleanup := func() {
  1430  		_ = remote.Delete(ref, regClientOpts...)
  1431  		ref, _ := ociremote.SignatureTag(ref.Context().Digest(remoteIndex.Descriptor.Digest.String()), ociremote.WithRemoteOptions(regClientOpts...))
  1432  		_ = remote.Delete(ref, regClientOpts...)
  1433  	}
  1434  	return ref, remoteIndex, cleanup
  1435  }
  1436  
  1437  func must(err error, t *testing.T) {
  1438  	t.Helper()
  1439  	if err != nil {
  1440  		t.Fatal(err)
  1441  	}
  1442  }
  1443  
  1444  func mustErr(err error, t *testing.T) {
  1445  	t.Helper()
  1446  	if err == nil {
  1447  		t.Fatal("expected error")
  1448  	}
  1449  }
  1450  
  1451  func equals(v1, v2 interface{}, t *testing.T) {
  1452  	if diff := cmp.Diff(v1, v2); diff != "" {
  1453  		t.Error(diff)
  1454  	}
  1455  }
  1456  
  1457  func reg(t *testing.T) (string, func()) {
  1458  	repo := os.Getenv("COSIGN_TEST_REPO")
  1459  	if repo != "" {
  1460  		return repo, func() {}
  1461  	}
  1462  
  1463  	t.Log("COSIGN_TEST_REPO unset, using fake registry")
  1464  	r := httptest.NewServer(registry.New())
  1465  	u, err := url.Parse(r.URL)
  1466  	if err != nil {
  1467  		t.Fatal(err)
  1468  	}
  1469  	return u.Host, r.Close
  1470  }
  1471  
  1472  func registryClientOpts(ctx context.Context) []remote.Option {
  1473  	return []remote.Option{
  1474  		remote.WithAuthFromKeychain(authn.DefaultKeychain),
  1475  		remote.WithContext(ctx),
  1476  	}
  1477  }
  1478  
  1479  // If a signature has a bundle, but *not for that signature*, cosign verification should fail
  1480  // This test is pretty long, so here are the basic points:
  1481  //  1. Sign image1 with a keypair, store entry in rekor
  1482  //  2. Sign image2 with keypair, DO NOT store entry in rekor
  1483  //  3. Take the bundle from image1 and store it on the signature in image2
  1484  //  4. Verification of image2 should now fail, since the bundle is for a different signature
  1485  func TestInvalidBundle(t *testing.T) {
  1486  	regName, stop := reg(t)
  1487  	defer stop()
  1488  	td := t.TempDir()
  1489  
  1490  	img1 := path.Join(regName, "cosign-e2e")
  1491  
  1492  	imgRef, _, cleanup := mkimage(t, img1)
  1493  	defer cleanup()
  1494  
  1495  	_, privKeyPath, pubKeyPath := keypair(t, td)
  1496  
  1497  	ctx := context.Background()
  1498  
  1499  	// Sign image1 and store the entry in rekor
  1500  	// (we're just using it for its bundle)
  1501  	defer setenv(t, env.VariableExperimental.String(), "1")()
  1502  	remoteOpts := ociremote.WithRemoteOptions(registryClientOpts(ctx)...)
  1503  	ko := options.KeyOpts{KeyRef: privKeyPath, PassFunc: passFunc, RekorURL: rekorURL}
  1504  	so := options.SignOptions{
  1505  		Upload: true,
  1506  		Force:  true,
  1507  	}
  1508  	must(sign.SignCmd(ro, ko, so, []string{img1}), t)
  1509  	// verify image1
  1510  	must(verify(pubKeyPath, img1, true, nil, ""), t)
  1511  	// extract the bundle from image1
  1512  	si, err := ociremote.SignedImage(imgRef, remoteOpts)
  1513  	must(err, t)
  1514  	imgSigs, err := si.Signatures()
  1515  	must(err, t)
  1516  	sigs, err := imgSigs.Get()
  1517  	must(err, t)
  1518  	if l := len(sigs); l != 1 {
  1519  		t.Error("expected one signature")
  1520  	}
  1521  	bund, err := sigs[0].Bundle()
  1522  	must(err, t)
  1523  	if bund == nil {
  1524  		t.Fail()
  1525  	}
  1526  
  1527  	// Now, we move on to image2
  1528  	// Sign image2 and DO NOT store the entry in rekor
  1529  	defer setenv(t, env.VariableExperimental.String(), "0")()
  1530  	img2 := path.Join(regName, "unrelated")
  1531  	imgRef2, _, cleanup := mkimage(t, img2)
  1532  	defer cleanup()
  1533  	so = options.SignOptions{
  1534  		Upload:       true,
  1535  		NoTlogUpload: true,
  1536  	}
  1537  	must(sign.SignCmd(ro, ko, so, []string{img2}), t)
  1538  	must(verify(pubKeyPath, img2, true, nil, ""), t)
  1539  
  1540  	si2, err := ociremote.SignedEntity(imgRef2, remoteOpts)
  1541  	must(err, t)
  1542  	sigs2, err := si2.Signatures()
  1543  	must(err, t)
  1544  	gottenSigs2, err := sigs2.Get()
  1545  	must(err, t)
  1546  	if len(gottenSigs2) != 1 {
  1547  		t.Fatal("there should be one signature")
  1548  	}
  1549  	sigsTag, err := ociremote.SignatureTag(imgRef2)
  1550  	if err != nil {
  1551  		t.Fatal(err)
  1552  	}
  1553  
  1554  	// At this point, we would mutate the signature to add the bundle annotation
  1555  	// since we don't have a function for it at the moment, mock this by deleting the signature
  1556  	// and pushing a new signature with the additional bundle annotation
  1557  	if err := remote.Delete(sigsTag); err != nil {
  1558  		t.Fatal(err)
  1559  	}
  1560  	mustErr(verify(pubKeyPath, img2, true, nil, ""), t)
  1561  
  1562  	newSig, err := mutate.Signature(gottenSigs2[0], mutate.WithBundle(bund))
  1563  	must(err, t)
  1564  	si2, err = ociremote.SignedEntity(imgRef2, remoteOpts)
  1565  	must(err, t)
  1566  	newImage, err := mutate.AttachSignatureToEntity(si2, newSig)
  1567  	must(err, t)
  1568  	if err := ociremote.WriteSignatures(sigsTag.Repository, newImage); err != nil {
  1569  		t.Fatal(err)
  1570  	}
  1571  
  1572  	// veriyfing image2 now should fail
  1573  	mustErr(verify(pubKeyPath, img2, true, nil, ""), t)
  1574  }
  1575  
  1576  func TestAttestBlobSignVerify(t *testing.T) {
  1577  	blob := "someblob"
  1578  	predicate := `{ "buildType": "x", "builder": { "id": "2" }, "recipe": {} }`
  1579  	predicateType := "slsaprovenance"
  1580  
  1581  	td1 := t.TempDir()
  1582  	t.Cleanup(func() {
  1583  		os.RemoveAll(td1)
  1584  	})
  1585  
  1586  	bp := filepath.Join(td1, blob)
  1587  	if err := os.WriteFile(bp, []byte(blob), 0644); err != nil {
  1588  		t.Fatal(err)
  1589  	}
  1590  
  1591  	anotherBlob := filepath.Join(td1, "another-blob")
  1592  	if err := os.WriteFile(anotherBlob, []byte("another-blob"), 0644); err != nil {
  1593  		t.Fatal(err)
  1594  	}
  1595  
  1596  	predicatePath := filepath.Join(td1, "predicate")
  1597  	if err := os.WriteFile(predicatePath, []byte(predicate), 0644); err != nil {
  1598  		t.Fatal(err)
  1599  	}
  1600  
  1601  	outputSignature := filepath.Join(td1, "signature")
  1602  
  1603  	_, privKeyPath1, pubKeyPath1 := keypair(t, td1)
  1604  
  1605  	ctx := context.Background()
  1606  	blobVerifyAttestationCmd := cliverify.VerifyBlobAttestationCommand{
  1607  		KeyRef:        pubKeyPath1,
  1608  		SignaturePath: outputSignature,
  1609  		PredicateType: predicateType,
  1610  	}
  1611  	// Verify should fail on a bad input
  1612  	mustErr(blobVerifyAttestationCmd.Exec(ctx, bp), t)
  1613  
  1614  	// Now attest the blob with the private key
  1615  	attestBlobCmd := attest.AttestBlobCommand{
  1616  		KeyRef:          privKeyPath1,
  1617  		PredicatePath:   predicatePath,
  1618  		PredicateType:   predicateType,
  1619  		OutputSignature: outputSignature,
  1620  		PassFunc:        passFunc,
  1621  	}
  1622  	must(attestBlobCmd.Exec(ctx, bp), t)
  1623  
  1624  	// Now verify should work
  1625  	must(blobVerifyAttestationCmd.Exec(ctx, bp), t)
  1626  
  1627  	// Make sure we fail with the wrong predicate type
  1628  	blobVerifyAttestationCmd.PredicateType = "custom"
  1629  	mustErr(blobVerifyAttestationCmd.Exec(ctx, bp), t)
  1630  
  1631  	// Make sure we fail with the wrong blob (set the predicate type back)
  1632  	blobVerifyAttestationCmd.PredicateType = predicateType
  1633  	mustErr(blobVerifyAttestationCmd.Exec(ctx, anotherBlob), t)
  1634  }