1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
30 type Appendable interface {
31 oci.SignedEntity
32 mutate.Appendable
33 }
34
35
36 type IndexAddendum struct {
37 Add Appendable
38 v1.Descriptor
39 }
40
41
42
43
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
60
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
72 func (i *indexWrapper) Signatures() (oci.Signatures, error) {
73 return empty.Signatures(), nil
74 }
75
76
77 func (i *indexWrapper) Attestations() (oci.Signatures, error) {
78 return empty.Signatures(), nil
79 }
80
81
82 func (*indexWrapper) Attachment(name string) (oci.File, error) {
83 return nil, errors.New("unimplemented")
84 }
85
86
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
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
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
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
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
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
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
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
212 func (si *signedImage) Signatures() (oci.Signatures, error) {
213 return si.so.dedupeAndReplace(si.sig, si.SignedImage.Signatures)
214 }
215
216
217 func (si *signedImage) Attestations() (oci.Signatures, error) {
218 return si.so.dedupeAndReplace(si.att, si.SignedImage.Attestations)
219 }
220
221
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
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
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
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
271 func (sii *signedImageIndex) Signatures() (oci.Signatures, error) {
272 return sii.so.dedupeAndReplace(sii.sig, sii.ociSignedImageIndex.Signatures)
273 }
274
275
276 func (sii *signedImageIndex) Attestations() (oci.Signatures, error) {
277 return sii.so.dedupeAndReplace(sii.att, sii.ociSignedImageIndex.Attestations)
278 }
279
280
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
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
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
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
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
341 func (si *signedUnknown) Signatures() (oci.Signatures, error) {
342 return si.so.dedupeAndReplace(si.sig, si.SignedEntity.Signatures)
343 }
344
345
346 func (si *signedUnknown) Attestations() (oci.Signatures, error) {
347 return si.so.dedupeAndReplace(si.att, si.SignedEntity.Attestations)
348 }
349
350
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
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