Skip to content

Commit de3655a

Browse files
author
Patrick Zheng
authored
feat: artifactType support in signature manifest (#542)
Signed-off-by: Patrick Zheng <[email protected]>
1 parent 02cc632 commit de3655a

File tree

3 files changed

+240
-61
lines changed

3 files changed

+240
-61
lines changed

registry/mediatype.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
package registry
1515

1616
// ArtifactTypeNotation specifies the artifact type for a notation object.
17-
// spec: https://github.com/notaryproject/notaryproject/blob/efc828223710f99ab9639d2d0f72d59036a8e80c/specs/signature-specification.md#storage
17+
// spec: https://github.com/notaryproject/specifications/blob/v1.1.0/specs/signature-specification.md#signature
1818
const ArtifactTypeNotation = "application/vnd.cncf.notary.signature"

registry/repository.go

Lines changed: 40 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,17 @@
1414
package registry
1515

1616
import (
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
5338
type 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
205187
func (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
244198
func 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

Comments
 (0)