...

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

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

     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  package mutate
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  
    22  	v1 "github.com/google/go-containerregistry/pkg/v1"
    23  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    24  	"github.com/sigstore/cosign/v2/pkg/oci"
    25  	"github.com/sigstore/cosign/v2/pkg/oci/empty"
    26  	"github.com/sigstore/cosign/v2/pkg/oci/signed"
    27  )
    28  
    29  // Appendable is our signed version of mutate.Appendable
    30  type Appendable interface {
    31  	oci.SignedEntity
    32  	mutate.Appendable
    33  }
    34  
    35  // IndexAddendum is our signed version of mutate.IndexAddendum
    36  type IndexAddendum struct {
    37  	Add Appendable
    38  	v1.Descriptor
    39  }
    40  
    41  // AppendManifests is a form of mutate.AppendManifests that produces an
    42  // oci.SignedImageIndex.  The index itself will contain no signatures,
    43  // but allows access to the contained signed entities.
    44  func AppendManifests(base v1.ImageIndex, adds ...IndexAddendum) oci.SignedImageIndex {
    45  	madds := make([]mutate.IndexAddendum, 0, len(adds))
    46  	for _, add := range adds {
    47  		madds = append(madds, mutate.IndexAddendum{
    48  			Add:        add.Add,
    49  			Descriptor: add.Descriptor,
    50  		})
    51  	}
    52  	return &indexWrapper{
    53  		v1Index:  mutate.AppendManifests(base, madds...),
    54  		ogbase:   base,
    55  		addendum: adds,
    56  	}
    57  }
    58  
    59  // We alias ImageIndex so that we can inline it without the type
    60  // name colliding with the name of a method it had to implement.
    61  type v1Index v1.ImageIndex
    62  
    63  type indexWrapper struct {
    64  	v1Index
    65  	ogbase   v1Index
    66  	addendum []IndexAddendum
    67  }
    68  
    69  var _ oci.SignedImageIndex = (*indexWrapper)(nil)
    70  
    71  // Signatures implements oci.SignedImageIndex
    72  func (i *indexWrapper) Signatures() (oci.Signatures, error) {
    73  	return empty.Signatures(), nil
    74  }
    75  
    76  // Attestations implements oci.SignedImageIndex
    77  func (i *indexWrapper) Attestations() (oci.Signatures, error) {
    78  	return empty.Signatures(), nil
    79  }
    80  
    81  // Attachment implements oci.SignedImage
    82  func (*indexWrapper) Attachment(name string) (oci.File, error) { //nolint: revive
    83  	return nil, errors.New("unimplemented")
    84  }
    85  
    86  // SignedImage implements oci.SignedImageIndex
    87  func (i *indexWrapper) SignedImage(h v1.Hash) (oci.SignedImage, error) {
    88  	for _, add := range i.addendum {
    89  		si, ok := add.Add.(oci.SignedImage)
    90  		if !ok {
    91  			continue
    92  		}
    93  		if d, err := si.Digest(); err != nil {
    94  			return nil, err
    95  		} else if d == h {
    96  			return si, nil
    97  		}
    98  	}
    99  
   100  	if sb, ok := i.ogbase.(oci.SignedImageIndex); ok {
   101  		return sb.SignedImage(h)
   102  	}
   103  
   104  	unsigned, err := i.Image(h)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	return signed.Image(unsigned), nil
   109  }
   110  
   111  // SignedImageIndex implements oci.SignedImageIndex
   112  func (i *indexWrapper) SignedImageIndex(h v1.Hash) (oci.SignedImageIndex, error) {
   113  	for _, add := range i.addendum {
   114  		sii, ok := add.Add.(oci.SignedImageIndex)
   115  		if !ok {
   116  			continue
   117  		}
   118  		if d, err := sii.Digest(); err != nil {
   119  			return nil, err
   120  		} else if d == h {
   121  			return sii, nil
   122  		}
   123  	}
   124  
   125  	if sb, ok := i.ogbase.(oci.SignedImageIndex); ok {
   126  		return sb.SignedImageIndex(h)
   127  	}
   128  
   129  	unsigned, err := i.ImageIndex(h)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return signed.ImageIndex(unsigned), nil
   134  }
   135  
   136  // AttachSignatureToEntity attaches the provided signature to the provided entity.
   137  func AttachSignatureToEntity(se oci.SignedEntity, sig oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
   138  	switch obj := se.(type) {
   139  	case oci.SignedImage:
   140  		return AttachSignatureToImage(obj, sig, opts...)
   141  	case oci.SignedImageIndex:
   142  		return AttachSignatureToImageIndex(obj, sig, opts...)
   143  	default:
   144  		return AttachSignatureToUnknown(obj, sig, opts...)
   145  	}
   146  }
   147  
   148  // AttachAttestationToEntity attaches the provided attestation to the provided entity.
   149  func AttachAttestationToEntity(se oci.SignedEntity, att oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
   150  	switch obj := se.(type) {
   151  	case oci.SignedImage:
   152  		return AttachAttestationToImage(obj, att, opts...)
   153  	case oci.SignedImageIndex:
   154  		return AttachAttestationToImageIndex(obj, att, opts...)
   155  	default:
   156  		return AttachAttestationToUnknown(obj, att, opts...)
   157  	}
   158  }
   159  
   160  // AttachFileToEntity attaches the provided file to the provided entity.
   161  func AttachFileToEntity(se oci.SignedEntity, name string, f oci.File, opts ...SignOption) (oci.SignedEntity, error) {
   162  	switch obj := se.(type) {
   163  	case oci.SignedImage:
   164  		return AttachFileToImage(obj, name, f, opts...)
   165  	case oci.SignedImageIndex:
   166  		return AttachFileToImageIndex(obj, name, f, opts...)
   167  	default:
   168  		return AttachFileToUnknown(obj, name, f, opts...)
   169  	}
   170  }
   171  
   172  // AttachSignatureToImage attaches the provided signature to the provided image.
   173  func AttachSignatureToImage(si oci.SignedImage, sig oci.Signature, opts ...SignOption) (oci.SignedImage, error) {
   174  	return &signedImage{
   175  		SignedImage: si,
   176  		sig:         sig,
   177  		attachments: make(map[string]oci.File),
   178  		so:          makeSignOpts(opts...),
   179  	}, nil
   180  }
   181  
   182  // AttachAttestationToImage attaches the provided attestation to the provided image.
   183  func AttachAttestationToImage(si oci.SignedImage, att oci.Signature, opts ...SignOption) (oci.SignedImage, error) {
   184  	return &signedImage{
   185  		SignedImage: si,
   186  		att:         att,
   187  		attachments: make(map[string]oci.File),
   188  		so:          makeSignOpts(opts...),
   189  	}, nil
   190  }
   191  
   192  // AttachFileToImage attaches the provided file to the provided image.
   193  func AttachFileToImage(si oci.SignedImage, name string, f oci.File, opts ...SignOption) (oci.SignedImage, error) {
   194  	return &signedImage{
   195  		SignedImage: si,
   196  		attachments: map[string]oci.File{
   197  			name: f,
   198  		},
   199  		so: makeSignOpts(opts...),
   200  	}, nil
   201  }
   202  
   203  type signedImage struct {
   204  	oci.SignedImage
   205  	sig         oci.Signature
   206  	att         oci.Signature
   207  	so          *signOpts
   208  	attachments map[string]oci.File
   209  }
   210  
   211  // Signatures implements oci.SignedImage
   212  func (si *signedImage) Signatures() (oci.Signatures, error) {
   213  	return si.so.dedupeAndReplace(si.sig, si.SignedImage.Signatures)
   214  }
   215  
   216  // Attestations implements oci.SignedImage
   217  func (si *signedImage) Attestations() (oci.Signatures, error) {
   218  	return si.so.dedupeAndReplace(si.att, si.SignedImage.Attestations)
   219  }
   220  
   221  // Attachment implements oci.SignedImage
   222  func (si *signedImage) Attachment(attName string) (oci.File, error) {
   223  	if f, ok := si.attachments[attName]; ok {
   224  		return f, nil
   225  	}
   226  	return nil, fmt.Errorf("attachment %q not found", attName)
   227  }
   228  
   229  // AttachSignatureToImageIndex attaches the provided signature to the provided image index.
   230  func AttachSignatureToImageIndex(sii oci.SignedImageIndex, sig oci.Signature, opts ...SignOption) (oci.SignedImageIndex, error) {
   231  	return &signedImageIndex{
   232  		ociSignedImageIndex: sii,
   233  		sig:                 sig,
   234  		attachments:         make(map[string]oci.File),
   235  		so:                  makeSignOpts(opts...),
   236  	}, nil
   237  }
   238  
   239  // AttachAttestationToImageIndex attaches the provided attestation to the provided image index.
   240  func AttachAttestationToImageIndex(sii oci.SignedImageIndex, att oci.Signature, opts ...SignOption) (oci.SignedImageIndex, error) {
   241  	return &signedImageIndex{
   242  		ociSignedImageIndex: sii,
   243  		att:                 att,
   244  		attachments:         make(map[string]oci.File),
   245  		so:                  makeSignOpts(opts...),
   246  	}, nil
   247  }
   248  
   249  // AttachFileToImageIndex attaches the provided file to the provided image index.
   250  func AttachFileToImageIndex(sii oci.SignedImageIndex, name string, f oci.File, opts ...SignOption) (oci.SignedImageIndex, error) {
   251  	return &signedImageIndex{
   252  		ociSignedImageIndex: sii,
   253  		attachments: map[string]oci.File{
   254  			name: f,
   255  		},
   256  		so: makeSignOpts(opts...),
   257  	}, nil
   258  }
   259  
   260  type ociSignedImageIndex oci.SignedImageIndex
   261  
   262  type signedImageIndex struct {
   263  	ociSignedImageIndex
   264  	sig         oci.Signature
   265  	att         oci.Signature
   266  	so          *signOpts
   267  	attachments map[string]oci.File
   268  }
   269  
   270  // Signatures implements oci.SignedImageIndex
   271  func (sii *signedImageIndex) Signatures() (oci.Signatures, error) {
   272  	return sii.so.dedupeAndReplace(sii.sig, sii.ociSignedImageIndex.Signatures)
   273  }
   274  
   275  // Attestations implements oci.SignedImageIndex
   276  func (sii *signedImageIndex) Attestations() (oci.Signatures, error) {
   277  	return sii.so.dedupeAndReplace(sii.att, sii.ociSignedImageIndex.Attestations)
   278  }
   279  
   280  // Attachment implements oci.SignedImageIndex
   281  func (sii *signedImageIndex) Attachment(attName string) (oci.File, error) {
   282  	if f, ok := sii.attachments[attName]; ok {
   283  		return f, nil
   284  	}
   285  	return nil, fmt.Errorf("attachment %q not found", attName)
   286  }
   287  
   288  // AttachSignatureToUnknown attaches the provided signature to the provided image.
   289  func AttachSignatureToUnknown(se oci.SignedEntity, sig oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
   290  	return &signedUnknown{
   291  		SignedEntity: se,
   292  		sig:          sig,
   293  		attachments:  make(map[string]oci.File),
   294  		so:           makeSignOpts(opts...),
   295  	}, nil
   296  }
   297  
   298  // AttachAttestationToUnknown attaches the provided attestation to the provided image.
   299  func AttachAttestationToUnknown(se oci.SignedEntity, att oci.Signature, opts ...SignOption) (oci.SignedEntity, error) {
   300  	return &signedUnknown{
   301  		SignedEntity: se,
   302  		att:          att,
   303  		attachments:  make(map[string]oci.File),
   304  		so:           makeSignOpts(opts...),
   305  	}, nil
   306  }
   307  
   308  // AttachFileToUnknown attaches the provided file to the provided image.
   309  func AttachFileToUnknown(se oci.SignedEntity, name string, f oci.File, opts ...SignOption) (oci.SignedEntity, error) {
   310  	return &signedUnknown{
   311  		SignedEntity: se,
   312  		attachments: map[string]oci.File{
   313  			name: f,
   314  		},
   315  		so: makeSignOpts(opts...),
   316  	}, nil
   317  }
   318  
   319  type signedUnknown struct {
   320  	oci.SignedEntity
   321  	sig         oci.Signature
   322  	att         oci.Signature
   323  	so          *signOpts
   324  	attachments map[string]oci.File
   325  }
   326  
   327  type digestable interface {
   328  	Digest() (v1.Hash, error)
   329  }
   330  
   331  // Digest is generally implemented by oci.SignedEntity implementations.
   332  func (si *signedUnknown) Digest() (v1.Hash, error) {
   333  	d, ok := si.SignedEntity.(digestable)
   334  	if !ok {
   335  		return v1.Hash{}, fmt.Errorf("underlying signed entity not digestable: %T", si.SignedEntity)
   336  	}
   337  	return d.Digest()
   338  }
   339  
   340  // Signatures implements oci.SignedEntity
   341  func (si *signedUnknown) Signatures() (oci.Signatures, error) {
   342  	return si.so.dedupeAndReplace(si.sig, si.SignedEntity.Signatures)
   343  }
   344  
   345  // Attestations implements oci.SignedEntity
   346  func (si *signedUnknown) Attestations() (oci.Signatures, error) {
   347  	return si.so.dedupeAndReplace(si.att, si.SignedEntity.Attestations)
   348  }
   349  
   350  // Attachment implements oci.SignedEntity
   351  func (si *signedUnknown) Attachment(attName string) (oci.File, error) {
   352  	if f, ok := si.attachments[attName]; ok {
   353  		return f, nil
   354  	}
   355  	return nil, fmt.Errorf("attachment %q not found", attName)
   356  }
   357  
   358  func (so *signOpts) dedupeAndReplace(sig oci.Signature, basefn func() (oci.Signatures, error)) (oci.Signatures, error) {
   359  	base, err := basefn()
   360  	if err != nil {
   361  		return nil, err
   362  	} else if sig == nil {
   363  		return base, nil
   364  	}
   365  	if so.dd != nil {
   366  		if existing, err := so.dd.Find(base, sig); err != nil {
   367  			return nil, err
   368  		} else if existing != nil {
   369  			// Just return base if the signature is redundant
   370  			return base, nil
   371  		}
   372  	}
   373  	if so.ro != nil {
   374  		replace, err := so.ro.Replace(base, sig)
   375  		if err != nil {
   376  			return nil, err
   377  		}
   378  		return ReplaceSignatures(replace)
   379  	}
   380  	return AppendSignatures(base, so.rct, sig)
   381  }
   382  

View as plain text