1414package registry
1515
1616import (
17- "bytes"
1817 "context"
1918 "encoding/json"
20- "errors"
2119 "fmt"
2220 "os"
2321
22+ "github.com/notaryproject/notation-go/log"
2423 "github.com/notaryproject/notation-go/registry/internal/artifactspec"
2524 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2625 "oras.land/oras-go/v2"
2726 "oras.land/oras-go/v2/content"
2827 "oras.land/oras-go/v2/content/oci"
29- "oras.land/oras-go/v2/errdef"
3028 "oras.land/oras-go/v2/registry"
3129)
3230
@@ -35,19 +33,6 @@ const (
3533 maxManifestSizeLimit = 4 * 1024 * 1024 // 4 MiB
3634)
3735
38- var (
39- // notationEmptyConfigDesc is the descriptor of an empty notation manifest
40- // config
41- // reference: https://github.com/notaryproject/specifications/blob/v1.0.0/specs/signature-specification.md#storage
42- notationEmptyConfigDesc = ocispec.Descriptor {
43- MediaType : ArtifactTypeNotation ,
44- Digest : ocispec .DescriptorEmptyJSON .Digest ,
45- Size : ocispec .DescriptorEmptyJSON .Size ,
46- }
47- // notationEmptyConfigData is the data of an empty notation manifest config
48- notationEmptyConfigData = ocispec .DescriptorEmptyJSON .Data
49- )
50-
5136// RepositoryOptions provides user options when creating a [Repository]
5237// it is kept for future extensibility
5338type RepositoryOptions struct {}
@@ -108,7 +93,6 @@ func (c *repositoryClient) ListSignatures(ctx context.Context, desc ocispec.Desc
10893 if repo , ok := c .GraphTarget .(registry.ReferrerLister ); ok {
10994 return repo .Referrers (ctx , desc , ArtifactTypeNotation , fn )
11095 }
111-
11296 signatureManifests , err := signatureReferrers (ctx , c .GraphTarget , desc )
11397 if err != nil {
11498 return fmt .Errorf ("failed to get referrers during ListSignatures due to %w" , err )
@@ -126,7 +110,6 @@ func (c *repositoryClient) FetchSignatureBlob(ctx context.Context, desc ocispec.
126110 if sigBlobDesc .Size > maxBlobSizeLimit {
127111 return nil , ocispec.Descriptor {}, fmt .Errorf ("signature blob too large: %d bytes" , sigBlobDesc .Size )
128112 }
129-
130113 var fetcher content.Fetcher = c .GraphTarget
131114 if repo , ok := c .GraphTarget .(registry.Repository ); ok {
132115 fetcher = repo .Blobs ()
@@ -179,6 +162,7 @@ func (c *repositoryClient) getSignatureBlobDesc(ctx context.Context, sigManifest
179162
180163 // get the signature blob descriptor from signature manifest
181164 var signatureBlobs []ocispec.Descriptor
165+
182166 // OCI image manifest
183167 if sigManifestDesc .MediaType == ocispec .MediaTypeImageManifest {
184168 var sigManifest ocispec.Manifest
@@ -193,55 +177,27 @@ func (c *repositoryClient) getSignatureBlobDesc(ctx context.Context, sigManifest
193177 }
194178 signatureBlobs = sigManifest .Blobs
195179 }
196-
197180 if len (signatureBlobs ) != 1 {
198181 return ocispec.Descriptor {}, fmt .Errorf ("signature manifest requries exactly one signature envelope blob, got %d" , len (signatureBlobs ))
199182 }
200-
201183 return signatureBlobs [0 ], nil
202184}
203185
204186// uploadSignatureManifest uploads the signature manifest to the registry
205187func (c * repositoryClient ) uploadSignatureManifest (ctx context.Context , subject , blobDesc ocispec.Descriptor , annotations map [string ]string ) (ocispec.Descriptor , error ) {
206- configDesc , err := pushNotationManifestConfig (ctx , c .GraphTarget )
207- if err != nil {
208- return ocispec.Descriptor {}, fmt .Errorf ("failed to push notation manifest config: %w" , err )
209- }
210-
211188 opts := oras.PackManifestOptions {
212189 Subject : & subject ,
213190 ManifestAnnotations : annotations ,
214191 Layers : []ocispec.Descriptor {blobDesc },
215- ConfigDescriptor : & configDesc ,
216- }
217-
218- return oras .PackManifest (ctx , c .GraphTarget , oras .PackManifestVersion1_1 , "" , opts )
219- }
220-
221- // pushNotationManifestConfig pushes an empty notation manifest config, if it
222- // doesn't exist.
223- //
224- // if the config exists, it returns the descriptor of the config without error.
225- func pushNotationManifestConfig (ctx context.Context , pusher content.Storage ) (ocispec.Descriptor , error ) {
226- // check if the config exists
227- exists , err := pusher .Exists (ctx , notationEmptyConfigDesc )
228- if err != nil {
229- return ocispec.Descriptor {}, fmt .Errorf ("unable to verify existence: %s: %s. Details: %w" , notationEmptyConfigDesc .Digest .String (), notationEmptyConfigDesc .MediaType , err )
230- }
231- if exists {
232- return notationEmptyConfigDesc , nil
233- }
234-
235- // return nil if the config pushed successfully or it already exists
236- if err := pusher .Push (ctx , notationEmptyConfigDesc , bytes .NewReader (notationEmptyConfigData )); err != nil && ! errors .Is (err , errdef .ErrAlreadyExists ) {
237- return ocispec.Descriptor {}, fmt .Errorf ("unable to push: %s: %s. Details: %w" , notationEmptyConfigDesc .Digest .String (), notationEmptyConfigDesc .MediaType , err )
238192 }
239- return notationEmptyConfigDesc , nil
193+ return oras . PackManifest ( ctx , c . GraphTarget , oras . PackManifestVersion1_1 , ArtifactTypeNotation , opts )
240194}
241195
242196// signatureReferrers returns referrer nodes of desc in target filtered by
243197// the "application/vnd.cncf.notary.signature" artifact type
244198func signatureReferrers (ctx context.Context , target content.ReadOnlyGraphStorage , desc ocispec.Descriptor ) ([]ocispec.Descriptor , error ) {
199+ logger := log .GetLogger (ctx )
200+
245201 var results []ocispec.Descriptor
246202 predecessors , err := target .Predecessors (ctx , desc )
247203 if err != nil {
@@ -257,13 +213,18 @@ func signatureReferrers(ctx context.Context, target content.ReadOnlyGraphStorage
257213 if err != nil {
258214 return nil , err
259215 }
216+
260217 var artifact artifactspec.Artifact
261218 if err := json .Unmarshal (fetched , & artifact ); err != nil {
262219 return nil , err
263220 }
264221 if artifact .Subject == nil || ! content .Equal (* artifact .Subject , desc ) {
265222 continue
266223 }
224+ if artifact .ArtifactType != ArtifactTypeNotation {
225+ // not a valid Notary Project signature
226+ continue
227+ }
267228 node .ArtifactType = artifact .ArtifactType
268229 node .Annotations = artifact .Annotations
269230 case ocispec .MediaTypeImageManifest :
@@ -274,22 +235,47 @@ func signatureReferrers(ctx context.Context, target content.ReadOnlyGraphStorage
274235 if err != nil {
275236 return nil , err
276237 }
238+
277239 var image ocispec.Manifest
278240 if err := json .Unmarshal (fetched , & image ); err != nil {
279241 return nil , err
280242 }
281243 if image .Subject == nil || ! content .Equal (* image .Subject , desc ) {
282244 continue
283245 }
284- node .ArtifactType = image .Config .MediaType
246+
247+ // check if image is a valid Notary Project signature
248+ switch image .ArtifactType {
249+ case ArtifactTypeNotation :
250+ // 1. artifactType is "application/vnd.cncf.notary.signature",
251+ // and config.mediaType is "application/vnd.oci.empty.v1+json"
252+ if image .Config .MediaType == ocispec .MediaTypeEmptyJSON {
253+ node .ArtifactType = image .ArtifactType
254+ } else {
255+ // not a valid Notary Project signature
256+ logger .Warnf ("not a valid Notary Project signature with artifactType %q, but config.mediaType is %q" , image .ArtifactType , image .Config .MediaType )
257+ continue
258+ }
259+ case "" :
260+ // 2. artifacteType does not exist,
261+ // and config.mediaType is "application/vnd.cncf.notary.signature"
262+ if image .Config .MediaType == ArtifactTypeNotation {
263+ node .ArtifactType = image .Config .MediaType
264+ } else {
265+ // not a valid Notary Project signature
266+ continue
267+ }
268+ default :
269+ // not a valid Notary Project signature
270+ continue
271+ }
285272 node .Annotations = image .Annotations
286273 default :
287274 continue
288275 }
289- // only keep nodes of "application/vnd.cncf.notary.signature"
290- if node .ArtifactType == ArtifactTypeNotation {
291- results = append (results , node )
292- }
276+
277+ // add the node to results
278+ results = append (results , node )
293279 }
294280 return results , nil
295281}
0 commit comments