Skip to content

Commit 8850f72

Browse files
committed
feat(cli): add --payload-media-type to sign
This commit introduces a new `--payload-media-type` flag to the `cosign sign` command. When signing with a custom payload using the `--payload` flag, users can now also specify the MIME type of that payload via `--payload-media-type`. This media type is then stored in the `mediaType` field of the signature layer descriptor in the resulting OCI manifest. This allows consumers of the signature to correctly interpret the payload's content type. If `--payload` is not used, this flag is ignored. Signed-off-by: Alberto Gonzalez <[email protected]>
1 parent ade0d32 commit 8850f72

File tree

14 files changed

+82
-70
lines changed

14 files changed

+82
-70
lines changed

cmd/cosign/cli/attach/attach.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ func attachAttestation(ctx context.Context, remoteOpts []ociremote.Option, signe
8989
// each access.
9090
ref = digest // nolint
9191

92-
opts := []static.Option{static.WithLayerMediaType(types.DssePayloadType)}
93-
att, err := static.NewAttestation(payload, opts...)
92+
staticOpts := []static.StaticOption{static.WithLayerMediaType(types.DssePayloadType)}
93+
att, err := static.NewAttestation(payload, staticOpts...)
9494
if err != nil {
9595
return err
9696
}

cmd/cosign/cli/attest/attest.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
170170
return nil
171171
}
172172

173-
opts := []static.Option{static.WithLayerMediaType(types.DssePayloadType)}
173+
staticOpts := []static.StaticOption{static.WithLayerMediaType(types.DssePayloadType)}
174174
if sv.Cert != nil {
175-
opts = append(opts, static.WithCertChain(sv.Cert, sv.Chain))
175+
staticOpts = append(staticOpts, static.WithCertChain(sv.Cert, sv.Chain))
176176
}
177177
var timestampBytes []byte
178178
var tsaPayload []byte
@@ -206,7 +206,7 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
206206
}
207207
bundle := cbundle.TimestampToRFC3161Timestamp(timestampBytes)
208208

209-
opts = append(opts, static.WithRFC3161Timestamp(bundle))
209+
staticOpts = append(staticOpts, static.WithRFC3161Timestamp(bundle))
210210
}
211211

212212
predicateType, err := options.ParsePredicateType(c.PredicateType)
@@ -218,7 +218,7 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
218218
"predicateType": predicateType,
219219
}
220220
// Add predicateType as manifest annotation
221-
opts = append(opts, static.WithAnnotations(predicateTypeAnnotation))
221+
staticOpts = append(staticOpts, static.WithAnnotations(predicateTypeAnnotation))
222222

223223
// Check whether we should be uploading to the transparency log
224224
shouldUpload, err := sign.ShouldUploadToTlog(ctx, c.KeyOpts, digest, c.TlogUpload)
@@ -238,10 +238,10 @@ func (c *AttestCommand) Exec(ctx context.Context, imageRef string) error {
238238
if err != nil {
239239
return err
240240
}
241-
opts = append(opts, static.WithBundle(cbundle.EntryToBundle(rekorEntry)))
241+
staticOpts = append(staticOpts, static.WithBundle(cbundle.EntryToBundle(rekorEntry)))
242242
}
243243

244-
sig, err := static.NewAttestation(signedPayload, opts...)
244+
sig, err := static.NewAttestation(signedPayload, staticOpts...)
245245
if err != nil {
246246
return err
247247
}

cmd/cosign/cli/options/sign.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type SignOptions struct {
3030
OutputPayload string
3131
OutputCertificate string
3232
PayloadPath string
33+
PayloadMediaType string
3334
Recursive bool
3435
Attachment string
3536
SkipConfirmation bool
@@ -96,6 +97,8 @@ func (o *SignOptions) AddFlags(cmd *cobra.Command) {
9697
cmd.Flags().StringVar(&o.PayloadPath, "payload", "",
9798
"path to a payload file to use rather than generating one")
9899
// _ = cmd.MarkFlagFilename("payload") // no typical extensions
100+
cmd.Flags().StringVar(&o.PayloadMediaType, "payload-media-type", "",
101+
"media type of the payload. Ignored if --payload is not specified.")
99102

100103
cmd.Flags().BoolVarP(&o.Recursive, "recursive", "r", false,
101104
"if a multi-arch image is specified, additionally sign each discrete image")

cmd/cosign/cli/sign/sign.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/google/go-containerregistry/pkg/name"
3333
v1 "github.com/google/go-containerregistry/pkg/v1"
3434
"github.com/google/go-containerregistry/pkg/v1/remote"
35+
"github.com/google/go-containerregistry/pkg/v1/types"
3536
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
3637
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio/fulcioverifier"
3738
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
@@ -51,6 +52,7 @@ import (
5152
"github.com/sigstore/cosign/v2/pkg/oci"
5253
"github.com/sigstore/cosign/v2/pkg/oci/mutate"
5354
ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote"
55+
"github.com/sigstore/cosign/v2/pkg/oci/static"
5456
"github.com/sigstore/cosign/v2/pkg/oci/walk"
5557
sigs "github.com/sigstore/cosign/v2/pkg/signature"
5658
"github.com/sigstore/sigstore/pkg/cryptoutils"
@@ -233,7 +235,11 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko opti
233235
}
234236

235237
var s icos.Signer
236-
s = ipayload.NewSigner(sv)
238+
var staticOpts []static.StaticOption
239+
if signOpts.PayloadPath != "" && signOpts.PayloadMediaType != "" {
240+
staticOpts = append(staticOpts, static.WithLayerMediaType(types.MediaType(signOpts.PayloadMediaType)))
241+
}
242+
s = ipayload.NewSigner(sv, staticOpts)
237243
if sv.Cert != nil {
238244
s = ifulcio.NewSigner(s, sv.Cert, sv.Chain)
239245
}

cmd/cosign/cli/verify/verify_blob.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
113113

114114
// Keys are optional!
115115
var cert *x509.Certificate
116-
opts := make([]static.Option, 0)
116+
staticOpts := make([]static.StaticOption, 0)
117117
switch {
118118
case c.KeyRef != "":
119119
co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, c.KeyRef)
@@ -270,7 +270,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
270270
}
271271
cert = bundleCert
272272
}
273-
opts = append(opts, static.WithBundle(b.Bundle))
273+
staticOpts = append(staticOpts, static.WithBundle(b.Bundle))
274274
}
275275
if c.RFC3161TimestampPath != "" {
276276
var rfc3161Timestamp bundle.RFC3161Timestamp
@@ -281,7 +281,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
281281
if err := json.Unmarshal(ts, &rfc3161Timestamp); err != nil {
282282
return err
283283
}
284-
opts = append(opts, static.WithRFC3161Timestamp(&rfc3161Timestamp))
284+
staticOpts = append(staticOpts, static.WithRFC3161Timestamp(&rfc3161Timestamp))
285285
}
286286
// Set an SCT if provided via the CLI.
287287
if c.SCTRef != "" {
@@ -327,7 +327,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
327327
if err != nil {
328328
return err
329329
}
330-
opts = append(opts, static.WithCertChain(certPEM, chainPEM))
330+
staticOpts = append(staticOpts, static.WithCertChain(certPEM, chainPEM))
331331
}
332332

333333
// Ignore Signed Certificate Timestamp if the flag is set or a key is provided
@@ -342,7 +342,7 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
342342
if err != nil {
343343
return err
344344
}
345-
signature, err := static.NewSignature(blobBytes, sig, opts...)
345+
signature, err := static.NewSignature(blobBytes, sig, staticOpts...)
346346
if err != nil {
347347
return err
348348
}

cmd/cosign/cli/verify/verify_blob_attestation.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
121121

122122
// Keys are optional!
123123
var cert *x509.Certificate
124-
opts := make([]static.Option, 0)
124+
staticOpts := make([]static.StaticOption, 0)
125125
switch {
126126
case c.KeyRef != "":
127127
co.SigVerifier, err = sigs.PublicKeyFromKeyRef(ctx, c.KeyRef)
@@ -316,7 +316,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
316316
if err != nil {
317317
return fmt.Errorf("decoding signature: %w", err)
318318
}
319-
opts = append(opts, static.WithBundle(b.Bundle))
319+
staticOpts = append(staticOpts, static.WithBundle(b.Bundle))
320320
}
321321
if c.RFC3161TimestampPath != "" {
322322
var rfc3161Timestamp bundle.RFC3161Timestamp
@@ -327,7 +327,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
327327
if err := json.Unmarshal(ts, &rfc3161Timestamp); err != nil {
328328
return err
329329
}
330-
opts = append(opts, static.WithRFC3161Timestamp(&rfc3161Timestamp))
330+
staticOpts = append(staticOpts, static.WithRFC3161Timestamp(&rfc3161Timestamp))
331331
}
332332
// Set an SCT if provided via the CLI.
333333
if c.SCTRef != "" {
@@ -369,10 +369,10 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
369369
if err != nil {
370370
return err
371371
}
372-
opts = append(opts, static.WithCertChain(certPEM, chainPEM))
372+
staticOpts = append(staticOpts, static.WithCertChain(certPEM, chainPEM))
373373
}
374374

375-
signature, err := static.NewAttestation(encodedSig, opts...)
375+
signature, err := static.NewAttestation(encodedSig, staticOpts...)
376376
if err != nil {
377377
return err
378378
}

internal/pkg/cosign/payload/attestor.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ func (pa *payloadAttestor) DSSEAttest(ctx context.Context, payload io.Reader) (o
6767
return nil, nil, err
6868
}
6969

70-
opts := []static.Option{static.WithLayerMediaType(types.DssePayloadType)}
70+
staticOpts := []static.StaticOption{static.WithLayerMediaType(types.DssePayloadType)}
7171

72-
att, err := static.NewAttestation(envelopeJSON, opts...)
72+
att, err := static.NewAttestation(envelopeJSON, staticOpts...)
7373
if err != nil {
7474
return nil, nil, err
7575
}
@@ -81,9 +81,9 @@ func (pa *payloadAttestor) DSSEAttest(ctx context.Context, payload io.Reader) (o
8181
// Option types other than `signature.SignOption` and `signature.PublicKeyOption` cause a runtime panic.
8282
func NewDSSEAttestor(payloadType string,
8383
s signature.Signer,
84-
signAndPublicKeyOptions ...interface{}) cosign.DSSEAttestor {
84+
opts ...interface{}) cosign.DSSEAttestor {
8585
return &payloadAttestor{
86-
signer: newSigner(s, signAndPublicKeyOptions...),
86+
signer: newSigner(s, opts...),
8787
payloadType: payloadType,
8888
}
8989
}

internal/pkg/cosign/payload/signer.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type payloadSigner struct {
3333
payloadSigner signature.Signer
3434
payloadSignerOpts []signature.SignOption
3535
publicKeyProviderOpts []signature.PublicKeyOption
36+
staticOpts []static.StaticOption
3637
}
3738

3839
var _ cosign.Signer = (*payloadSigner)(nil)
@@ -53,7 +54,7 @@ func (ps *payloadSigner) Sign(ctx context.Context, payload io.Reader) (oci.Signa
5354
}
5455

5556
b64sig := base64.StdEncoding.EncodeToString(sig)
56-
ociSig, err := static.NewSignature(payloadBytes, b64sig)
57+
ociSig, err := static.NewSignature(payloadBytes, b64sig, ps.staticOpts...)
5758
if err != nil {
5859
return nil, nil, err
5960
}
@@ -82,33 +83,35 @@ func (ps *payloadSigner) signPayload(ctx context.Context, payloadBytes []byte) (
8283
return sig, nil
8384
}
8485

85-
func newSigner(s signature.Signer,
86-
signAndPublicKeyOptions ...interface{}) payloadSigner {
86+
func newSigner(s signature.Signer, opts ...interface{}) payloadSigner {
8787
var sOpts []signature.SignOption
8888
var pkOpts []signature.PublicKeyOption
89+
var staticOpts []static.StaticOption
8990

90-
for _, opt := range signAndPublicKeyOptions {
91+
for _, opt := range opts {
9192
switch o := opt.(type) {
9293
case signature.SignOption:
9394
sOpts = append(sOpts, o)
9495
case signature.PublicKeyOption:
9596
pkOpts = append(pkOpts, o)
97+
case static.StaticOption:
98+
staticOpts = append(staticOpts, o)
9699
default:
97-
panic(fmt.Sprintf("options must be of type `signature.SignOption` or `signature.PublicKeyOption`. Got a %T: %v", o, o))
100+
panic(fmt.Sprintf("options must be of type `signature.SignOption`, `signature.PublicKeyOption` or `static.Option`. Got a %T: %v", o, o))
98101
}
99102
}
100103

101104
return payloadSigner{
102105
payloadSigner: s,
106+
staticOpts: staticOpts,
103107
payloadSignerOpts: sOpts,
104108
publicKeyProviderOpts: pkOpts,
105109
}
106110
}
107111

108112
// NewSigner returns a `cosign.Signer` which uses the given `signature.Signer` to sign requested payloads.
109113
// Option types other than `signature.SignOption` and `signature.PublicKeyOption` cause a runtime panic.
110-
func NewSigner(s signature.Signer,
111-
signAndPublicKeyOptions ...interface{}) cosign.Signer {
112-
signer := newSigner(s, signAndPublicKeyOptions...)
114+
func NewSigner(s signature.Signer, opts ...interface{}) cosign.Signer {
115+
signer := newSigner(s, opts...)
113116
return &signer
114117
}

pkg/cosign/verify_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ func TestVerifyImageSignatureWithNoChain(t *testing.T) {
310310
rekorPubKeys := NewTrustedTransparencyLogPubKeys()
311311
rekorPubKeys.AddTransparencyLogPubKey(pemBytes, tuf.Active)
312312

313-
opts := []static.Option{static.WithCertChain(pemLeaf, []byte{}), static.WithBundle(rekorBundle)}
314-
ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature), opts...)
313+
staticOpts := []static.StaticOption{static.WithCertChain(pemLeaf, []byte{}), static.WithBundle(rekorBundle)}
314+
ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature), staticOpts...)
315315

316316
verified, err := VerifyImageSignature(context.TODO(), ociSig, v1.Hash{},
317317
&CheckOpts{
@@ -354,8 +354,8 @@ func TestVerifyImageSignatureWithInvalidPublicKeyType(t *testing.T) {
354354
// Add one valid key here.
355355
rekorPubKeys.AddTransparencyLogPubKey(pemBytes, tuf.Active)
356356

357-
opts := []static.Option{static.WithCertChain(pemLeaf, []byte{}), static.WithBundle(rekorBundle)}
358-
ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature), opts...)
357+
staticOpts := []static.StaticOption{static.WithCertChain(pemLeaf, []byte{}), static.WithBundle(rekorBundle)}
358+
ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature), staticOpts...)
359359

360360
// Then try to validate with keys that are not ecdsa.PublicKey and should
361361
// fail.

pkg/oci/mutate/signature_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ Hr/+CxFvaJWmpYqNkLDGRU+9orzh5hI2RrcuaQ==
6262
`)
6363
)
6464

65-
func mustCreateSignature(t *testing.T, payload []byte, b64sig string, opts ...static.Option) oci.Signature {
65+
func mustCreateSignature(t *testing.T, payload []byte, b64sig string, staticOpts ...static.StaticOption) oci.Signature {
6666
t.Helper()
67-
sig, err := static.NewSignature(payload, b64sig, opts...)
67+
sig, err := static.NewSignature(payload, b64sig, staticOpts...)
6868
if err != nil {
6969
t.Fatalf("failed to create static signature: %v", err)
7070
}

0 commit comments

Comments
 (0)