...

Source file src/github.com/sigstore/cosign/v2/pkg/oci/mutate/signature_test.go

Documentation: github.com/sigstore/cosign/v2/pkg/oci/mutate

     1  // Copyright 2021 The Sigstore Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mutate
    16  
    17  import (
    18  	"encoding/base64"
    19  	"io"
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	"github.com/google/go-containerregistry/pkg/v1/types"
    24  	"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
    25  	"github.com/sigstore/cosign/v2/pkg/oci"
    26  	"github.com/sigstore/cosign/v2/pkg/oci/static"
    27  )
    28  
    29  var (
    30  	testCertBytes = []byte(`
    31  -----BEGIN CERTIFICATE-----
    32  MIICjzCCAhSgAwIBAgITV2heiswW9YldtVEAu98QxDO8TTAKBggqhkjOPQQDAzAq
    33  MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
    34  MDkxNDE5MTI0MFoXDTIxMDkxNDE5MzIzOVowADBZMBMGByqGSM49AgEGCCqGSM49
    35  AwEHA0IABMF1AWZcfvubslc4ABNnvGbRjm6GWVHxrJ1RRthTHMCE4FpFmiHQBfGt
    36  6n80DqszGj77Whb35O33+Dal4Y2po+CjggFBMIIBPTAOBgNVHQ8BAf8EBAMCB4Aw
    37  EwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU340G
    38  3G1ozVNmFC5TBFV0yNuouvowHwYDVR0jBBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG
    39  0+wwgY0GCCsGAQUFBwEBBIGAMH4wfAYIKwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRl
    40  Y2EtY29udGVudC02MDNmZTdlNy0wMDAwLTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQu
    41  c3RvcmFnZS5nb29nbGVhcGlzLmNvbS9jYTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5j
    42  cnQwOAYDVR0RAQH/BC4wLIEqa2V5bGVzc0BkaXN0cm9sZXNzLmlhbS5nc2Vydmlj
    43  ZWFjY291bnQuY29tMAoGCCqGSM49BAMDA2kAMGYCMQDcH9cdkxW6ugsbPHqX9qrM
    44  wlMaprcwnlktS3+5xuABr5icuqwrB/Fj5doFtS7AnM0CMQD9MjSaUmHFFF7zoLMx
    45  uThR1Z6JuA21HwxtL3GyJ8UQZcEPOlTBV593HrSAwBhiCoY=
    46  -----END CERTIFICATE-----
    47  `)
    48  	testChainBytes = []byte(`
    49  -----BEGIN CERTIFICATE-----
    50  MIIB+DCCAX6gAwIBAgITNVkDZoCiofPDsy7dfm6geLbuhzAKBggqhkjOPQQDAzAq
    51  MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
    52  MDMwNzAzMjAyOVoXDTMxMDIyMzAzMjAyOVowKjEVMBMGA1UEChMMc2lnc3RvcmUu
    53  ZGV2MREwDwYDVQQDEwhzaWdzdG9yZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLSy
    54  A7Ii5k+pNO8ZEWY0ylemWDowOkNa3kL+GZE5Z5GWehL9/A9bRNA3RbrsZ5i0Jcas
    55  taRL7Sp5fp/jD5dxqc/UdTVnlvS16an+2Yfswe/QuLolRUCrcOE2+2iA5+tzd6Nm
    56  MGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYE
    57  FMjFHQBBmiQpMlEk6w2uSu1KBtPsMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2u
    58  Su1KBtPsMAoGCCqGSM49BAMDA2gAMGUCMH8liWJfMui6vXXBhjDgY4MwslmN/TJx
    59  Ve/83WrFomwmNf056y1X48F9c4m3a3ozXAIxAKjRay5/aj/jsKKGIkmQatjI8uup
    60  Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==
    61  -----END CERTIFICATE-----
    62  `)
    63  )
    64  
    65  func mustCreateSignature(t *testing.T, payload []byte, b64sig string, opts ...static.Option) oci.Signature {
    66  	t.Helper()
    67  	sig, err := static.NewSignature(payload, b64sig, opts...)
    68  	if err != nil {
    69  		t.Fatalf("failed to create static signature: %v", err)
    70  	}
    71  	return sig
    72  }
    73  
    74  func mustBase64Decode(t *testing.T, s string) []byte {
    75  	t.Helper()
    76  	b, err := base64.StdEncoding.DecodeString(s)
    77  	if err != nil {
    78  		t.Fatalf("failed to base64 decode: %v", err)
    79  	}
    80  	return b
    81  }
    82  
    83  func assertSignaturesEqual(t *testing.T, wanted, got oci.Signature) {
    84  	t.Helper()
    85  
    86  	t.Run("Payloads match", func(t *testing.T) {
    87  		t.Helper()
    88  		wantedPayload, err := wanted.Payload()
    89  		if err != nil {
    90  			t.Fatalf("wanted.Payload() returned error: %v", err)
    91  		}
    92  		gotPayload, err := got.Payload()
    93  		if err != nil {
    94  			t.Fatalf("got.Payload() returned error: %v", err)
    95  		}
    96  		if diff := cmp.Diff(wantedPayload, gotPayload); diff != "" {
    97  			t.Errorf("Payload() mismatch (-want +got):\n%s", diff)
    98  		}
    99  	})
   100  
   101  	t.Run("Base64Signatures match", func(t *testing.T) {
   102  		t.Helper()
   103  		wantedB64Sig, err := wanted.Base64Signature()
   104  		if err != nil {
   105  			t.Fatalf("wanted.Base64Signature() returned error: %v", err)
   106  		}
   107  		gotB64Sig, err := got.Base64Signature()
   108  		if err != nil {
   109  			t.Fatalf("got.Base64Signature() returned error: %v", err)
   110  		}
   111  		if diff := cmp.Diff(wantedB64Sig, gotB64Sig); diff != "" {
   112  			t.Errorf("Base64Signature() mismatch (-want +got):\n%s", diff)
   113  		}
   114  	})
   115  
   116  	t.Run("Bundles match", func(t *testing.T) {
   117  		t.Helper()
   118  		wantedBundle, err := wanted.Bundle()
   119  		if err != nil {
   120  			t.Fatalf("wanted.Bundle() returned error: %v", err)
   121  		}
   122  		gotBundle, err := got.Bundle()
   123  		if err != nil {
   124  			t.Fatalf("got.Bundle() returned error: %v", err)
   125  		}
   126  		if diff := cmp.Diff(wantedBundle, gotBundle); diff != "" {
   127  			t.Errorf("Bundle() mismatch (-want +got):\n%s", diff)
   128  		}
   129  	})
   130  
   131  	t.Run("RFC3161 timestamp bundles match", func(t *testing.T) {
   132  		t.Helper()
   133  		wantedBundle, err := wanted.RFC3161Timestamp()
   134  		if err != nil {
   135  			t.Fatalf("wanted.Bundle() returned error: %v", err)
   136  		}
   137  		gotBundle, err := got.RFC3161Timestamp()
   138  		if err != nil {
   139  			t.Fatalf("got.RFC3161Timestamp() returned error: %v", err)
   140  		}
   141  		if diff := cmp.Diff(wantedBundle, gotBundle); diff != "" {
   142  			t.Errorf("RFC3161Timestamp() mismatch (-want +got):\n%s", diff)
   143  		}
   144  	})
   145  
   146  	t.Run("Certs match", func(t *testing.T) {
   147  		t.Helper()
   148  		wantedCert, err := wanted.Cert()
   149  		if err != nil {
   150  			t.Fatalf("wanted.Bundle() returned error: %v", err)
   151  		}
   152  		gotCert, err := got.Cert()
   153  		if err != nil {
   154  			t.Fatalf("got.Cert() returned error: %v", err)
   155  		}
   156  		if diff := cmp.Diff(wantedCert, gotCert); diff != "" {
   157  			t.Errorf("Cert() mismatch (-want +got):\n%s", diff)
   158  		}
   159  	})
   160  
   161  	t.Run("Chains match", func(t *testing.T) {
   162  		t.Helper()
   163  		wantedChain, err := wanted.Chain()
   164  		if err != nil {
   165  			t.Fatalf("wanted.Bundle() returned error: %v", err)
   166  		}
   167  		gotChain, err := got.Chain()
   168  		if err != nil {
   169  			t.Fatalf("got.Chain() returned error: %v", err)
   170  		}
   171  		if diff := cmp.Diff(wantedChain, gotChain); diff != "" {
   172  			t.Errorf("Chain() mismatch (-want +got):\n%s", diff)
   173  		}
   174  	})
   175  
   176  	t.Run("MediaTypes match", func(t *testing.T) {
   177  		t.Helper()
   178  		wantedMediaType, err := wanted.MediaType()
   179  		if err != nil {
   180  			t.Fatalf("wanted.MediaType() returned error: %v", err)
   181  		}
   182  		gotMediaType, err := got.MediaType()
   183  		if err != nil {
   184  			t.Fatalf("got.MediaType() returned error: %v", err)
   185  		}
   186  		if diff := cmp.Diff(wantedMediaType, gotMediaType); diff != "" {
   187  			t.Errorf("MediaType() mismatch (-want +got):\n%s", diff)
   188  		}
   189  	})
   190  
   191  	var gotAnnotations map[string]string
   192  	t.Run("Annotations match", func(t *testing.T) {
   193  		t.Helper()
   194  		wantedAnnotations, err := wanted.Annotations()
   195  		if err != nil {
   196  			t.Fatalf("wanted.Annotations() returned error: %v", err)
   197  		}
   198  		gotAnnotations, err = got.Annotations()
   199  		if err != nil {
   200  			t.Fatalf("got.Annotations() returned error: %v", err)
   201  		}
   202  		if diff := cmp.Diff(wantedAnnotations, gotAnnotations); diff != "" {
   203  			t.Errorf("Annotations() mismatch (-want +got):\n%s", diff)
   204  		}
   205  	})
   206  
   207  	t.Run("DiffIDs match", func(t *testing.T) {
   208  		t.Helper()
   209  		wantedDiffID, err := wanted.DiffID()
   210  		if err != nil {
   211  			t.Fatalf("wanted.DiffID() returned error: %v", err)
   212  		}
   213  		gotDiffID, err := got.DiffID()
   214  		if err != nil {
   215  			t.Fatalf("got.DiffID() returned error: %v", err)
   216  		}
   217  		if wantedDiffID != gotDiffID {
   218  			t.Errorf("DiffID() mismatch. Wanted: %v, got: %v", wantedDiffID, gotDiffID)
   219  		}
   220  	})
   221  
   222  	t.Run("Sizes match", func(t *testing.T) {
   223  		t.Helper()
   224  		wantedSize, err := wanted.Size()
   225  		if err != nil {
   226  			t.Fatalf("wanted.Size() returned error: %v", err)
   227  		}
   228  		gotSize, err := got.Size()
   229  		if err != nil {
   230  			t.Fatalf("got.Size() returned error: %v", err)
   231  		}
   232  		if wantedSize != gotSize {
   233  			t.Errorf("Size() mismatch. Wanted: %v, got: %v", wantedSize, gotSize)
   234  		}
   235  	})
   236  
   237  	t.Run("Compressed values match", func(t *testing.T) {
   238  		t.Helper()
   239  		wantedCompReader, err := wanted.Compressed()
   240  		if err != nil {
   241  			t.Fatalf("wanted.Compressed() returned error: %v", err)
   242  		}
   243  		defer wantedCompReader.Close()
   244  		wantedCompressed, err := io.ReadAll(wantedCompReader)
   245  		if err != nil {
   246  			t.Fatalf("io.ReadAll(wanted.Compressed()) returned error: %v", err)
   247  		}
   248  		gotCompReader, err := got.Compressed()
   249  		if err != nil {
   250  			t.Fatalf("got.Compressed() returned error: %v", err)
   251  		}
   252  		defer gotCompReader.Close()
   253  		gotCompressed, err := io.ReadAll(gotCompReader)
   254  		if err != nil {
   255  			t.Fatalf("io.ReadAll(got.Compressed()) returned error: %v", err)
   256  		}
   257  		if diff := cmp.Diff(wantedCompressed, gotCompressed); diff != "" {
   258  			t.Errorf("MediaType() mismatch (-want +got):\n%s", diff)
   259  		}
   260  	})
   261  
   262  	t.Run("Uncompressed values match", func(t *testing.T) {
   263  		t.Helper()
   264  		wantedUncompReader, err := wanted.Uncompressed()
   265  		if err != nil {
   266  			t.Fatalf("wanted.Uncompressed() returned error: %v", err)
   267  		}
   268  		defer wantedUncompReader.Close()
   269  		wantedUncompressed, err := io.ReadAll(wantedUncompReader)
   270  		if err != nil {
   271  			t.Fatalf("io.ReadAll(wanted.Uncompressed()) returned error: %v", err)
   272  		}
   273  		gotUncompReader, err := got.Uncompressed()
   274  		if err != nil {
   275  			t.Fatalf("got.Compressed() returned error: %v", err)
   276  		}
   277  		defer gotUncompReader.Close()
   278  		gotUncompressed, err := io.ReadAll(gotUncompReader)
   279  		if err != nil {
   280  			t.Fatalf("io.ReadAll(got.Uncompressed()) returned error: %v", err)
   281  		}
   282  		if diff := cmp.Diff(wantedUncompressed, gotUncompressed); diff != "" {
   283  			t.Errorf("MediaType() mismatch (-want +got):\n%s", diff)
   284  		}
   285  	})
   286  }
   287  
   288  func TestSignatureWithAnnotations(t *testing.T) {
   289  	payload := "this is the TestSignatureWithAnnotations content!"
   290  	b64sig := "b64 content1="
   291  	annotations := map[string]string{
   292  		"foo":  "bar",
   293  		"test": "yes",
   294  	}
   295  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   296  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig, static.WithAnnotations(annotations))
   297  
   298  	newSig, err := Signature(originalSig, WithAnnotations(annotations))
   299  	if err != nil {
   300  		t.Fatalf("Signature(WithAnnotations()) returned error: %v", err)
   301  	}
   302  
   303  	assertSignaturesEqual(t, expectedSig, newSig)
   304  }
   305  
   306  func TestSignatureWithBundle(t *testing.T) {
   307  	payload := "this is the TestSignatureWithBundle content!"
   308  	b64sig := "b64 content2="
   309  	b := &bundle.RekorBundle{
   310  		SignedEntryTimestamp: mustBase64Decode(t, "MEUCIEDcarEwRYkrxE9ne+kzEVvUhnWaauYzxhUyXOLy1hwAAiEA4VdVCvNRs+D/5o33C2KBy+q2YX3lP4Y7nqRFU+K3hi0="),
   311  		Payload: bundle.RekorPayload{
   312  			Body:           "REMOVED",
   313  			IntegratedTime: 1631646761,
   314  			LogIndex:       693591,
   315  			LogID:          "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
   316  		},
   317  	}
   318  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   319  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig, static.WithBundle(b))
   320  
   321  	newSig, err := Signature(originalSig, WithBundle(b))
   322  	if err != nil {
   323  		t.Fatalf("Signature(WithBundle()) returned error: %v", err)
   324  	}
   325  
   326  	assertSignaturesEqual(t, expectedSig, newSig)
   327  }
   328  
   329  func TestSignatureWithRFC3161Timestamp(t *testing.T) {
   330  	payload := "this is the TestSignatureWithBundle content!"
   331  	b64sig := "b64 content2="
   332  	b := &bundle.RFC3161Timestamp{
   333  		SignedRFC3161Timestamp: mustBase64Decode(t, "MEUCIQClUkUqZNf+6dxBc/pxq22JIluTB7Kmip1G0FIF5E0C1wIgLqXm+IM3JYW/P/qjMZSXW+J8bt5EOqNfe3R+0A9ooFE="),
   334  	}
   335  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   336  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig, static.WithRFC3161Timestamp(b))
   337  
   338  	newSig, err := Signature(originalSig, WithRFC3161Timestamp(b))
   339  	if err != nil {
   340  		t.Fatalf("Signature(WithRFC3161Timestamp()) returned error: %v", err)
   341  	}
   342  
   343  	assertSignaturesEqual(t, expectedSig, newSig)
   344  }
   345  
   346  func TestSignatureWithCertChain(t *testing.T) {
   347  	payload := "this is the TestSignatureWithCertChain content!"
   348  	b64sig := "b64 content3="
   349  
   350  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   351  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig, static.WithCertChain(testCertBytes, testChainBytes))
   352  
   353  	newSig, err := Signature(originalSig, WithCertChain(testCertBytes, testChainBytes))
   354  	if err != nil {
   355  		t.Fatalf("Signature(WithCertChain()) returned error: %v", err)
   356  	}
   357  
   358  	assertSignaturesEqual(t, expectedSig, newSig)
   359  }
   360  
   361  func TestSignatureWithMediaType(t *testing.T) {
   362  	payload := "this is the TestSignatureWithMediaType content!"
   363  	b64sig := "b64 content4="
   364  	mediaType := types.MediaType("test/media.type")
   365  
   366  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   367  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig, static.WithLayerMediaType(mediaType))
   368  
   369  	newSig, err := Signature(originalSig, WithMediaType(mediaType))
   370  	if err != nil {
   371  		t.Fatalf("Signature(WithMediaType()) returned error: %v", err)
   372  	}
   373  
   374  	assertSignaturesEqual(t, expectedSig, newSig)
   375  }
   376  
   377  func TestSignatureWithEverything(t *testing.T) {
   378  	payload := "this is the TestSignatureWithEverything content!"
   379  	b64sig := "b64 content5="
   380  	annotations := map[string]string{
   381  		"foo":  "bar",
   382  		"test": "yes",
   383  	}
   384  	b := &bundle.RekorBundle{
   385  		SignedEntryTimestamp: mustBase64Decode(t, "MEUCIQClUkUqZNf+6dxBc/pxq22JIluTB7Kmip1G0FIF5E0C1wIgLqXm+IM3JYW/P/qjMZSXW+J8bt5EOqNfe3R+0A9ooFE="),
   386  		Payload: bundle.RekorPayload{
   387  			Body:           "REMOVED",
   388  			IntegratedTime: 1631646761,
   389  			LogIndex:       693591,
   390  			LogID:          "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
   391  		},
   392  	}
   393  	mediaType := types.MediaType("test/media.type")
   394  
   395  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   396  
   397  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig,
   398  		static.WithAnnotations(annotations),
   399  		static.WithBundle(b),
   400  		static.WithCertChain(testCertBytes, testChainBytes),
   401  		static.WithLayerMediaType(mediaType))
   402  
   403  	newSig, err := Signature(originalSig,
   404  		WithAnnotations(annotations),
   405  		WithBundle(b),
   406  		WithCertChain(testCertBytes, testChainBytes),
   407  		WithMediaType(mediaType))
   408  
   409  	if err != nil {
   410  		t.Fatalf("Signature(With...) returned error: %v", err)
   411  	}
   412  
   413  	assertSignaturesEqual(t, expectedSig, newSig)
   414  }
   415  
   416  func TestSignatureWithEverythingTSA(t *testing.T) {
   417  	payload := "this is the TestSignatureWithEverything content!"
   418  	b64sig := "b64 content5="
   419  	annotations := map[string]string{
   420  		"foo":  "bar",
   421  		"test": "yes",
   422  	}
   423  	b := &bundle.RFC3161Timestamp{
   424  		SignedRFC3161Timestamp: mustBase64Decode(t, "MEUCIQClUkUqZNf+6dxBc/pxq22JIluTB7Kmip1G0FIF5E0C1wIgLqXm+IM3JYW/P/qjMZSXW+J8bt5EOqNfe3R+0A9ooFE="),
   425  	}
   426  	mediaType := types.MediaType("test/media.type")
   427  
   428  	originalSig := mustCreateSignature(t, []byte(payload), b64sig)
   429  
   430  	expectedSig := mustCreateSignature(t, []byte(payload), b64sig,
   431  		static.WithAnnotations(annotations),
   432  		static.WithRFC3161Timestamp(b),
   433  		static.WithCertChain(testCertBytes, testChainBytes),
   434  		static.WithLayerMediaType(mediaType))
   435  
   436  	newSig, err := Signature(originalSig,
   437  		WithAnnotations(annotations),
   438  		WithRFC3161Timestamp(b),
   439  		WithCertChain(testCertBytes, testChainBytes),
   440  		WithMediaType(mediaType))
   441  
   442  	if err != nil {
   443  		t.Fatalf("Signature(With...) returned error: %v", err)
   444  	}
   445  
   446  	assertSignaturesEqual(t, expectedSig, newSig)
   447  }
   448  

View as plain text