1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package remote
17
18 import (
19 "errors"
20 "fmt"
21 "io"
22 "net/http"
23
24 "github.com/google/go-containerregistry/pkg/name"
25 v1 "github.com/google/go-containerregistry/pkg/v1"
26 "github.com/google/go-containerregistry/pkg/v1/remote"
27 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
28 "github.com/google/go-containerregistry/pkg/v1/types"
29 payloadsize "github.com/sigstore/cosign/v2/internal/pkg/cosign/payload/size"
30 ociexperimental "github.com/sigstore/cosign/v2/internal/pkg/oci/remote"
31 "github.com/sigstore/cosign/v2/pkg/oci"
32 )
33
34
35 var (
36 remoteImage = remote.Image
37 remoteIndex = remote.Index
38 remoteGet = remote.Get
39 remoteWrite = remote.Write
40 )
41
42
43
44 type EntityNotFoundError struct {
45 baseErr error
46 }
47
48 func (e *EntityNotFoundError) Error() string {
49 return fmt.Sprintf("entity not found in registry, error: %v", e.baseErr)
50 }
51
52 func NewEntityNotFoundError(err error) error {
53 return &EntityNotFoundError{
54 baseErr: err,
55 }
56 }
57
58
59
60 func SignedEntity(ref name.Reference, options ...Option) (oci.SignedEntity, error) {
61 o := makeOptions(ref.Context(), options...)
62
63 got, err := remoteGet(ref, o.ROpt...)
64 var te *transport.Error
65 if errors.As(err, &te) && te.StatusCode == http.StatusNotFound {
66 return nil, NewEntityNotFoundError(err)
67 } else if err != nil {
68 return nil, err
69 }
70
71 switch got.MediaType {
72 case types.OCIImageIndex, types.DockerManifestList:
73 ii, err := got.ImageIndex()
74 if err != nil {
75 return nil, err
76 }
77 return &index{
78 v1Index: ii,
79 ref: ref.Context().Digest(got.Digest.String()),
80 opt: o,
81 }, nil
82
83 case types.OCIManifestSchema1, types.DockerManifestSchema2:
84 i, err := got.Image()
85 if err != nil {
86 return nil, err
87 }
88 return &image{
89 Image: i,
90 opt: o,
91 }, nil
92
93 default:
94 return nil, fmt.Errorf("unknown mime type: %v", got.MediaType)
95 }
96 }
97
98
99
100 func normalize(h v1.Hash, prefix string, suffix string) string {
101 return normalizeWithSeparator(h, prefix, suffix, "-")
102 }
103
104
105
106 func normalizeWithSeparator(h v1.Hash, prefix string, suffix string, algorithmSeparator string) string {
107 if suffix == "" {
108 return fmt.Sprint(prefix, h.Algorithm, algorithmSeparator, h.Hex)
109 }
110 return fmt.Sprint(prefix, h.Algorithm, algorithmSeparator, h.Hex, ".", suffix)
111 }
112
113
114 func SignatureTag(ref name.Reference, opts ...Option) (name.Tag, error) {
115 o := makeOptions(ref.Context(), opts...)
116 return suffixTag(ref, o.SignatureSuffix, "-", o)
117 }
118
119
120 func AttestationTag(ref name.Reference, opts ...Option) (name.Tag, error) {
121 o := makeOptions(ref.Context(), opts...)
122 return suffixTag(ref, o.AttestationSuffix, "-", o)
123 }
124
125
126 func SBOMTag(ref name.Reference, opts ...Option) (name.Tag, error) {
127 o := makeOptions(ref.Context(), opts...)
128 return suffixTag(ref, o.SBOMSuffix, "-", o)
129 }
130
131
132 func DigestTag(ref name.Reference, opts ...Option) (name.Tag, error) {
133 o := makeOptions(ref.Context(), opts...)
134 return suffixTag(ref, "", ":", o)
135 }
136
137
138
139
140
141 func DockerContentDigest(ref name.Tag, opts ...Option) (name.Tag, error) {
142 o := makeOptions(ref.Context(), opts...)
143 desc, err := remoteGet(ref, o.ROpt...)
144 if err != nil {
145 return name.Tag{}, err
146 }
147 h := desc.Digest
148 return o.TargetRepository.Tag(normalizeWithSeparator(h, o.TagPrefix, "", ":")), nil
149 }
150
151 func suffixTag(ref name.Reference, suffix string, algorithmSeparator string, o *options) (name.Tag, error) {
152 var h v1.Hash
153 if digest, ok := ref.(name.Digest); ok {
154 var err error
155 h, err = v1.NewHash(digest.DigestStr())
156 if err != nil {
157 return name.Tag{}, err
158 }
159 } else {
160 desc, err := remoteGet(ref, o.ROpt...)
161 if err != nil {
162 return name.Tag{}, err
163 }
164 h = desc.Digest
165 }
166 return o.TargetRepository.Tag(normalizeWithSeparator(h, o.TagPrefix, suffix, algorithmSeparator)), nil
167 }
168
169
170 func signatures(digestable oci.SignedEntity, o *options) (oci.Signatures, error) {
171 h, err := digestable.Digest()
172 if err != nil {
173 return nil, err
174 }
175 return Signatures(o.TargetRepository.Tag(normalize(h, o.TagPrefix, o.SignatureSuffix)), o.OriginalOptions...)
176 }
177
178
179 func attestations(digestable oci.SignedEntity, o *options) (oci.Signatures, error) {
180 h, err := digestable.Digest()
181 if err != nil {
182 return nil, err
183 }
184 return Signatures(o.TargetRepository.Tag(normalize(h, o.TagPrefix, o.AttestationSuffix)), o.OriginalOptions...)
185 }
186
187
188 func attachment(digestable oci.SignedEntity, attName string, o *options) (oci.File, error) {
189
190 if file, err := attachmentExperimentalOCI(digestable, attName, o); err == nil {
191 return file, nil
192 }
193
194 h, err := digestable.Digest()
195 if err != nil {
196 return nil, err
197 }
198 img, err := SignedImage(o.TargetRepository.Tag(normalize(h, o.TagPrefix, attName)), o.OriginalOptions...)
199 if err != nil {
200 return nil, err
201 }
202 ls, err := img.Layers()
203 if err != nil {
204 return nil, err
205 }
206 if len(ls) != 1 {
207 return nil, fmt.Errorf("expected exactly one layer in attachment, got %d", len(ls))
208 }
209
210 return &attached{
211 SignedImage: img,
212 layer: ls[0],
213 }, nil
214 }
215
216 type attached struct {
217 oci.SignedImage
218 layer v1.Layer
219 }
220
221 var _ oci.File = (*attached)(nil)
222
223
224 func (f *attached) FileMediaType() (types.MediaType, error) {
225 return f.layer.MediaType()
226 }
227
228
229 func (f *attached) Payload() ([]byte, error) {
230 size, err := f.layer.Size()
231 if err != nil {
232 return nil, err
233 }
234 err = payloadsize.CheckSize(uint64(size))
235 if err != nil {
236 return nil, err
237 }
238
239
240
241
242
243 rc, err := f.layer.Compressed()
244 if err != nil {
245 return nil, err
246 }
247 defer rc.Close()
248 return io.ReadAll(rc)
249 }
250
251
252 func attachmentExperimentalOCI(digestable oci.SignedEntity, attName string, o *options) (oci.File, error) {
253 h, err := digestable.Digest()
254 if err != nil {
255 return nil, err
256 }
257 d := o.TargetRepository.Digest(h.String())
258
259 artifactType := ociexperimental.ArtifactType(attName)
260 index, err := Referrers(d, artifactType, o.OriginalOptions...)
261 if err != nil {
262 return nil, err
263 }
264 results := index.Manifests
265
266 numResults := len(results)
267 if numResults == 0 {
268 return nil, fmt.Errorf("unable to locate reference with artifactType %s", artifactType)
269 } else if numResults > 1 {
270
271
272 fmt.Printf("WARNING: there were a total of %d references with artifactType %s\n", numResults, artifactType)
273 }
274
275 lastResult := results[numResults-1]
276
277 img, err := SignedImage(o.TargetRepository.Digest(lastResult.Digest.String()), o.OriginalOptions...)
278 if err != nil {
279 return nil, err
280 }
281 ls, err := img.Layers()
282 if err != nil {
283 return nil, err
284 }
285 if len(ls) != 1 {
286 return nil, fmt.Errorf("expected exactly one layer in attachment, got %d", len(ls))
287 }
288 return &attached{
289 SignedImage: img,
290 layer: ls[0],
291 }, nil
292 }
293
View as plain text