From 9a2524ebf306bc3f6380832474b663b5814820c0 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Thu, 25 Jan 2024 22:17:42 -0800 Subject: [PATCH 01/14] Add support for signing blob Signed-off-by: Pritesh Bandi --- notation.go | 73 ++++++++++++++++++++++----- notation_test.go | 98 ++++++++++++++++++++++++++++++++++++- signer/plugin.go | 111 ++++++++++++++++++++++++++++++++---------- signer/plugin_test.go | 26 +++++----- signer/signer.go | 22 ++++++++- 5 files changed, 277 insertions(+), 53 deletions(-) diff --git a/notation.go b/notation.go index ef8593c7..868d7d62 100644 --- a/notation.go +++ b/notation.go @@ -22,9 +22,13 @@ import ( "encoding/json" "errors" "fmt" + "io" + "mime" "strings" "time" + orasRegistry "oras.land/oras-go/v2/registry" + "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-go/internal/envelope" "github.com/notaryproject/notation-go/log" @@ -32,7 +36,6 @@ import ( "github.com/notaryproject/notation-go/verifier/trustpolicy" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - orasRegistry "oras.land/oras-go/v2/registry" ) var errDoneVerification = errors.New("done verification") @@ -65,6 +68,12 @@ type Signer interface { Sign(ctx context.Context, desc ocispec.Descriptor, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error) } +type BlobSigner interface { + // SignBlob signs the artifact described by its descriptor, + // and returns the signature and SignerInfo. + SignBlob(ctx context.Context, reader io.Reader, opts SignBlobOptions) ([]byte, *signature.SignerInfo, error) +} + // signerAnnotation facilitates return of manifest annotations by signers type signerAnnotation interface { // PluginAnnotations returns signature manifest annotations returned from @@ -85,22 +94,26 @@ type SignOptions struct { UserMetadata map[string]string } +type SignBlobOptions struct { + SignerSignOptions + + ContentMediaType string + + // UserMetadata contains key-value pairs that are added to the signature + // payload + UserMetadata map[string]string +} + // Sign signs the artifact and push the signature to the Repository. -// The descriptor of the sign content is returned upon sucessful signing. +// The descriptor of the sign content is returned upon successful signing. func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts SignOptions) (ocispec.Descriptor, error) { - // sanity check - if signer == nil { - return ocispec.Descriptor{}, errors.New("signer cannot be nil") + // sanity checks + if err := validate(signer, signOpts.SignerSignOptions); err != nil { + return ocispec.Descriptor{}, err } if repo == nil { return ocispec.Descriptor{}, errors.New("repo cannot be nil") } - if signOpts.ExpiryDuration < 0 { - return ocispec.Descriptor{}, fmt.Errorf("expiry duration cannot be a negative value") - } - if signOpts.ExpiryDuration%time.Second != 0 { - return ocispec.Descriptor{}, fmt.Errorf("expiry duration supports minimum granularity of seconds") - } logger := log.GetLogger(ctx) artifactRef := signOpts.ArtifactReference @@ -152,6 +165,44 @@ func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts return targetDesc, nil } +func SignBlob(ctx context.Context, signer BlobSigner, reader io.Reader, signOpts SignBlobOptions) ([]byte, error) { + // sanity checks + if err := validate(signer, signOpts.SignerSignOptions); err != nil { + return nil, err + } + if reader == nil { + return nil, errors.New("reader cannot be nil") + } + if signOpts.ContentMediaType != "" { + if _, _, err := mime.ParseMediaType(signOpts.ContentMediaType); err != nil { + return nil, fmt.Errorf("invalid content media-type '%s': %v", signOpts.ContentMediaType, err) + } + } + + sig, _, err := signer.SignBlob(ctx, reader, signOpts) + if err != nil { + return nil, err + } + + return sig, nil +} + +func validate(signer any, signOpts SignerSignOptions) error { + if signer == nil { + return errors.New("signer cannot be nil") + } + if signOpts.ExpiryDuration < 0 { + return fmt.Errorf("expiry duration cannot be a negative value") + } + if signOpts.ExpiryDuration%time.Second != 0 { + return fmt.Errorf("expiry duration supports minimum granularity of seconds") + } + if _, _, err := mime.ParseMediaType(signOpts.SignatureMediaType); err != nil { + return fmt.Errorf("invalid signature media-type '%s': %v", signOpts.SignatureMediaType, err) + } + return nil +} + func addUserMetadataToDescriptor(ctx context.Context, desc ocispec.Descriptor, userMetadata map[string]string) (ocispec.Descriptor, error) { logger := log.GetLogger(ctx) diff --git a/notation_test.go b/notation_test.go index 31264cee..f63971c4 100644 --- a/notation_test.go +++ b/notation_test.go @@ -17,14 +17,17 @@ import ( "context" "errors" "fmt" + "io" "math" "os" "path/filepath" + "strings" "testing" "time" "github.com/notaryproject/notation-core-go/signature" "github.com/notaryproject/notation-core-go/signature/cose" + "github.com/notaryproject/notation-core-go/signature/jws" "github.com/notaryproject/notation-go/internal/mock" "github.com/notaryproject/notation-go/plugin" "github.com/notaryproject/notation-go/registry" @@ -47,6 +50,7 @@ func TestSignSuccess(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(b *testing.T) { opts := SignOptions{} + opts.SignatureMediaType = jws.MediaTypeEnvelope opts.ExpiryDuration = tc.dur opts.ArtifactReference = mock.SampleArtifactUri @@ -58,11 +62,86 @@ func TestSignSuccess(t *testing.T) { } } +func TestSignBlobSuccess(t *testing.T) { + reader := strings.NewReader("some content") + testCases := []struct { + name string + dur time.Duration + mtype string + agent string + pConfig map[string]string + metadata map[string]string + }{ + {"expiryInHours", 24 * time.Hour, "video/mp4", "", nil, nil}, + {"oneSecondExpiry", 1 * time.Second, "video/mp4", "", nil, nil}, + {"zeroExpiry", 0, "video/mp4", "", nil, nil}, + //{"invalidContentMediaType", 1 * time.Second, "zap/zop/sop", nil, nil}, + {"validContentType", 1 * time.Second, "video/mp4", "", nil, nil}, + {"emptyContentType", 1 * time.Second, "", "", nil, nil}, + {"emptyContentType", 1 * time.Second, "video/mp4", "someDummyAgent", map[string]string{"hi": "hello"}, map[string]string{"bye": "tata"}}, + } + for _, tc := range testCases { + t.Run(tc.name, func(b *testing.T) { + opts := SignBlobOptions{ + SignerSignOptions: SignerSignOptions{ + SignatureMediaType: jws.MediaTypeEnvelope, + ExpiryDuration: tc.dur, + PluginConfig: tc.pConfig, + SigningAgent: tc.agent, + }, + UserMetadata: expectedMetadata, + ContentMediaType: tc.mtype, + } + + _, err := SignBlob(context.Background(), &dummySigner{}, reader, opts) + if err != nil { + b.Fatalf("Sign failed with error: %v", err) + } + }) + } +} + +func TestSignError(t *testing.T) { + reader := strings.NewReader("some content") + testCases := []struct { + name string + signer BlobSigner + dur time.Duration + rdr io.Reader + mtype string + }{ + {"negativeExpiry", &dummySigner{}, -1 * time.Second, nil, "video/mp4"}, + {"milliSecExpiry", &dummySigner{}, 1 * time.Millisecond, nil, "video/mp4"}, + {"invalidContentMediaType", &dummySigner{}, 1 * time.Second, reader, "video/mp4/zoping"}, + {"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4"}, + {"nilSigner", nil, 1 * time.Second, reader, "video/mp4"}, + {"signerError", &dummySigner{fail: true}, 1 * time.Second, reader, "video/mp4"}, + } + for _, tc := range testCases { + t.Run(tc.name, func(b *testing.T) { + opts := SignBlobOptions{ + SignerSignOptions: SignerSignOptions{ + SignatureMediaType: jws.MediaTypeEnvelope, + ExpiryDuration: tc.dur, + PluginConfig: nil, + }, + ContentMediaType: tc.mtype, + } + + _, err := SignBlob(context.Background(), tc.signer, tc.rdr, opts) + if err == nil { + b.Error("expected error but didnt found") + } + }) + } +} + func TestSignSuccessWithUserMetadata(t *testing.T) { repo := mock.NewRepository() opts := SignOptions{} opts.ArtifactReference = mock.SampleArtifactUri opts.UserMetadata = expectedMetadata + opts.SignatureMediaType = jws.MediaTypeEnvelope _, err := Sign(context.Background(), &verifyMetadataSigner{}, repo, opts) if err != nil { @@ -182,6 +261,9 @@ func TestSignDigestNotMatchResolve(t *testing.T) { repo := mock.NewRepository() repo.MissMatchDigest = true signOpts := SignOptions{ + SignerSignOptions: SignerSignOptions{ + SignatureMediaType: jws.MediaTypeEnvelope, + }, ArtifactReference: mock.SampleArtifactUri, } @@ -320,7 +402,9 @@ func dummyPolicyStatement() (policyStatement trustpolicy.TrustPolicy) { return } -type dummySigner struct{} +type dummySigner struct { + fail bool +} func (s *dummySigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error) { return []byte("ABC"), &signature.SignerInfo{ @@ -330,6 +414,18 @@ func (s *dummySigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts Si }, nil } +func (s *dummySigner) SignBlob(ctx context.Context, reader io.Reader, opts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { + if s.fail { + return nil, nil, errors.New("expected SignBlob failure") + } + + return []byte("ABC"), &signature.SignerInfo{ + SignedAttributes: signature.SignedAttributes{ + SigningTime: time.Now(), + }, + }, nil +} + type verifyMetadataSigner struct{} func (s *verifyMetadataSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error) { diff --git a/signer/plugin.go b/signer/plugin.go index 3bf14321..21883e1b 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -19,6 +19,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "time" "oras.land/oras-go/v2/content" @@ -29,12 +30,13 @@ import ( "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation-go/plugin/proto" "github.com/notaryproject/notation-plugin-framework-go/plugin" + "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -// pluginSigner signs artifacts and generates signatures. +// PluginSigner signs artifacts and generates signatures. // It implements notation.Signer -type pluginSigner struct { +type PluginSigner struct { plugin plugin.SignPlugin keyID string pluginConfig map[string]string @@ -44,7 +46,7 @@ type pluginSigner struct { // NewFromPlugin creates a notation.Signer that signs artifacts and generates // signatures by delegating the one or more operations to the named plugin, // as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces. -func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (notation.Signer, error) { +func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (*PluginSigner, error) { if plugin == nil { return nil, errors.New("nil plugin") } @@ -52,7 +54,7 @@ func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[stri return nil, errors.New("keyID not specified") } - return &pluginSigner{ + return &PluginSigner{ plugin: plugin, keyID: keyID, pluginConfig: pluginConfig, @@ -60,57 +62,92 @@ func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[stri } // PluginAnnotations returns signature manifest annotations returned from plugin -func (s *pluginSigner) PluginAnnotations() map[string]string { +func (s *PluginSigner) PluginAnnotations() map[string]string { return s.manifestAnnotations } // Sign signs the artifact described by its descriptor and returns the // marshalled envelope. -func (s *pluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) + mergedConfig := s.mergeConfig(opts.PluginConfig) + logger.Debug("Invoking plugin's get-plugin-metadata command") - req := &plugin.GetMetadataRequest{ - PluginConfig: s.mergeConfig(opts.PluginConfig), - } - metadata, err := s.plugin.GetMetadata(ctx, req) + metadata, err := s.plugin.GetMetadata(ctx, &proto.GetMetadataRequest{PluginConfig: mergedConfig}) if err != nil { return nil, nil, err } logger.Debugf("Using plugin %v with capabilities %v to sign artifact %v in signature media type %v", metadata.Name, metadata.Capabilities, desc.Digest, opts.SignatureMediaType) - if metadata.HasCapability(plugin.CapabilitySignatureGenerator) { - return s.generateSignature(ctx, desc, opts, metadata) - } else if metadata.HasCapability(plugin.CapabilityEnvelopeGenerator) { + if metadata.HasCapability(proto.CapabilitySignatureGenerator) { + // Get key info and validate. + ks, err := s.getKeySpec(ctx, mergedConfig) + if err != nil { + return nil, nil, err + } + return s.generateSignature(ctx, desc, opts, ks, metadata) + } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { return s.generateSignatureEnvelope(ctx, desc, opts) } return nil, nil, fmt.Errorf("plugin does not have signing capabilities") } -func (s *pluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, metadata *plugin.GetMetadataResponse) ([]byte, *signature.SignerInfo, error) { +// SignBlob signs the arbitrary data and returns the marshalled envelope. +func (s *PluginSigner) SignBlob(ctx context.Context, reader io.Reader, opts notation.SignBlobOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) - logger.Debug("Generating signature by plugin") - config := s.mergeConfig(opts.PluginConfig) - // Get key info. - key, err := s.describeKey(ctx, config) + mergedConfig := s.mergeConfig(opts.PluginConfig) + + logger.Debug("Invoking plugin's get-plugin-metadata command") + metadata, err := s.plugin.GetMetadata(ctx, &proto.GetMetadataRequest{PluginConfig: mergedConfig}) if err != nil { return nil, nil, err } - // Check keyID is honored. - if s.keyID != key.KeyID { - return nil, nil, fmt.Errorf("keyID in describeKey response %q does not match request %q", key.KeyID, s.keyID) + // Get key info and validate. + ks, err := s.getKeySpec(ctx, mergedConfig) + if err != nil { + return nil, nil, err } - ks, err := proto.DecodeKeySpec(key.KeySpec) + + desc, err := getDescriptor(reader, opts.ContentMediaType, ks) if err != nil { return nil, nil, err } + logger.Debugf("Using plugin %v with capabilities %v to sign artifact using descriptor %+v", metadata.Name, metadata.Capabilities, desc) + if metadata.HasCapability(proto.CapabilitySignatureGenerator) { + return s.generateSignature(ctx, desc, opts.SignerSignOptions, ks, metadata) + } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { + return s.generateSignatureEnvelope(ctx, desc, opts.SignerSignOptions) + } + return nil, nil, fmt.Errorf("plugin does not have signing capabilities") +} + +func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) (signature.KeySpec, error) { + // Get key info and validate. + logger := log.GetLogger(ctx) + logger.Debug("Invoking plugin's describe-key command") + descKeyResp, err := s.describeKey(ctx, config) + if err != nil { + return signature.KeySpec{}, err + } + + if s.keyID != descKeyResp.KeyID { + return signature.KeySpec{}, fmt.Errorf("keyID in describeKey response %q does not match request %q", descKeyResp.KeyID, s.keyID) + } + + return proto.DecodeKeySpec(descKeyResp.KeySpec) +} + +func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, ks signature.KeySpec, metadata *proto.GetMetadataResponse) ([]byte, *signature.SignerInfo, error) { + logger := log.GetLogger(ctx) + logger.Debug("Generating signature by plugin") genericSigner := genericSigner{ Signer: &pluginPrimitiveSigner{ ctx: ctx, plugin: s.plugin, keyID: s.keyID, - pluginConfig: config, + pluginConfig: s.mergeConfig(opts.PluginConfig), keySpec: ks, }, } @@ -119,7 +156,7 @@ func (s *pluginSigner) generateSignature(ctx context.Context, desc ocispec.Descr return genericSigner.Sign(ctx, desc, opts) } -func (s *pluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debug("Generating signature envelope by plugin") payload := envelope.Payload{TargetArtifact: envelope.SanitizeTargetArtifact(desc)} @@ -181,7 +218,7 @@ func (s *pluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocisp return resp.SignatureEnvelope, &envContent.SignerInfo, nil } -func (s *pluginSigner) mergeConfig(config map[string]string) map[string]string { +func (s *PluginSigner) mergeConfig(config map[string]string) map[string]string { c := make(map[string]string, len(s.pluginConfig)+len(config)) // First clone s.PluginConfig. for k, v := range s.pluginConfig { @@ -194,8 +231,28 @@ func (s *pluginSigner) mergeConfig(config map[string]string) map[string]string { return c } -func (s *pluginSigner) describeKey(ctx context.Context, config map[string]string) (*plugin.DescribeKeyResponse, error) { - req := &plugin.DescribeKeyRequest{ +func getDescriptor(reader io.Reader, mediaType string, ks signature.KeySpec) (ocispec.Descriptor, error) { + hashAlgo := ks.SignatureAlgorithm().Hash() + h := hashAlgo.HashFunc().New() + bytes, err := io.Copy(h, reader) + if err != nil { + return ocispec.Descriptor{}, err + } + + hash := h.Sum(nil) + if mediaType == "" { + mediaType = "application/octet-stream" + } + + return ocispec.Descriptor{ + MediaType: mediaType, + Digest: digest.FromBytes(hash), + Size: bytes, + }, nil +} + +func (s *PluginSigner) describeKey(ctx context.Context, config map[string]string) (*proto.DescribeKeyResponse, error) { + req := &proto.DescribeKeyRequest{ KeyID: s.keyID, PluginConfig: config, } diff --git a/signer/plugin_test.go b/signer/plugin_test.go index c3dd2b30..41e51340 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -136,7 +136,7 @@ func (p *mockPlugin) GenerateSignature(ctx context.Context, req *proto.GenerateS // GenerateEnvelope generates the Envelope with signature based on the request. func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *proto.GenerateEnvelopeRequest) (*proto.GenerateEnvelopeResponse, error) { - internalPluginSigner := pluginSigner{ + internalPluginSigner := PluginSigner{ plugin: newMockPlugin(p.key, p.certs, p.keySpec), } @@ -214,7 +214,7 @@ func TestNewFromPluginFailed(t *testing.T) { } func TestSigner_Sign_EnvelopeNotSupported(t *testing.T) { - signer := pluginSigner{ + signer := PluginSigner{ plugin: newMockPlugin(nil, nil, signature.KeySpec{Type: signature.KeyTypeRSA, Size: 2048}), } opts := notation.SignerSignOptions{SignatureMediaType: "unsupported"} @@ -225,7 +225,7 @@ func TestSigner_Sign_DescribeKeyIDMismatch(t *testing.T) { respKeyId := "" for _, envelopeType := range signature.RegisteredEnvelopeTypes() { t.Run(fmt.Sprintf("envelopeType=%v", envelopeType), func(t *testing.T) { - signer := pluginSigner{ + signer := PluginSigner{ plugin: newMockPlugin(nil, nil, signature.KeySpec{}), keyID: "1", } @@ -238,7 +238,7 @@ func TestSigner_Sign_ExpiryInValid(t *testing.T) { for _, envelopeType := range signature.RegisteredEnvelopeTypes() { t.Run(fmt.Sprintf("envelopeType=%v", envelopeType), func(t *testing.T) { ks, _ := signature.ExtractKeySpec(keyCertPairCollections[0].certs[0]) - signer := pluginSigner{ + signer := PluginSigner{ plugin: newMockPlugin(keyCertPairCollections[0].key, keyCertPairCollections[0].certs, ks), } _, _, err := signer.Sign(context.Background(), ocispec.Descriptor{}, notation.SignerSignOptions{ExpiryDuration: -24 * time.Hour, SignatureMediaType: envelopeType}) @@ -255,7 +255,7 @@ func TestSigner_Sign_InvalidCertChain(t *testing.T) { t.Run(fmt.Sprintf("envelopeType=%v", envelopeType), func(t *testing.T) { mockPlugin := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) mockPlugin.invalidCertChain = true - signer := pluginSigner{ + signer := PluginSigner{ plugin: mockPlugin, } testSignerError(t, signer, "x509: malformed certificate", notation.SignerSignOptions{SignatureMediaType: envelopeType}) @@ -269,7 +269,7 @@ func TestSigner_Sign_InvalidDescriptor(t *testing.T) { mockPlugin := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) mockPlugin.wantEnvelope = true mockPlugin.invalidDescriptor = true - signer := pluginSigner{ + signer := PluginSigner{ plugin: mockPlugin, } testSignerError(t, signer, "during signing, following unknown attributes were added to subject descriptor: [\"additional_field\"]", notation.SignerSignOptions{SignatureMediaType: envelopeType}) @@ -282,7 +282,7 @@ func TestPluginSigner_Sign_SignatureVerifyError(t *testing.T) { t.Run(fmt.Sprintf("envelopeType=%v", envelopeType), func(t *testing.T) { mockPlugin := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) mockPlugin.invalidSig = true - signer := pluginSigner{ + signer := PluginSigner{ plugin: mockPlugin, } testSignerError(t, signer, "signature is invalid", notation.SignerSignOptions{SignatureMediaType: envelopeType}) @@ -295,7 +295,7 @@ func TestPluginSigner_Sign_Valid(t *testing.T) { for _, keyCert := range keyCertPairCollections { t.Run(fmt.Sprintf("external plugin,envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { keySpec, _ := proto.DecodeKeySpec(proto.KeySpec(keyCert.keySpecName)) - pluginSigner := pluginSigner{ + pluginSigner := PluginSigner{ plugin: newMockPlugin(keyCert.key, keyCert.certs, keySpec), } basicSignTest(t, &pluginSigner, envelopeType, &validMetadata) @@ -311,7 +311,7 @@ func TestPluginSigner_SignEnvelope_RunFailed(t *testing.T) { wantEnvelope: true, failEnvelope: true, } - signer := pluginSigner{ + signer := PluginSigner{ plugin: p, } testSignerError(t, signer, "failed GenerateEnvelope", notation.SignerSignOptions{SignatureMediaType: envelopeType}) @@ -326,7 +326,7 @@ func TestPluginSigner_SignEnvelope_Valid(t *testing.T) { keySpec, _ := proto.DecodeKeySpec(proto.KeySpec(keyCert.keySpecName)) mockPlugin := newMockPlugin(keyCert.key, keyCert.certs, keySpec) mockPlugin.wantEnvelope = true - pluginSigner := pluginSigner{ + pluginSigner := PluginSigner{ plugin: mockPlugin, } basicSignTest(t, &pluginSigner, envelopeType, &validMetadata) @@ -341,7 +341,7 @@ func TestPluginSigner_SignWithAnnotations_Valid(t *testing.T) { t.Run(fmt.Sprintf("external plugin,envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { keySpec, _ := proto.DecodeKeySpec(proto.KeySpec(keyCert.keySpecName)) annts := map[string]string{"key": "value"} - pluginSigner := pluginSigner{ + pluginSigner := PluginSigner{ plugin: &mockPlugin{ key: keyCert.key, certs: keyCert.certs, @@ -360,7 +360,7 @@ func TestPluginSigner_SignWithAnnotations_Valid(t *testing.T) { } } -func testSignerError(t *testing.T, signer pluginSigner, wantEr string, opts notation.SignerSignOptions) { +func testSignerError(t *testing.T, signer PluginSigner, wantEr string, opts notation.SignerSignOptions) { t.Helper() _, _, err := signer.Sign(context.Background(), ocispec.Descriptor{}, opts) if err == nil || !strings.Contains(err.Error(), wantEr) { @@ -368,7 +368,7 @@ func testSignerError(t *testing.T, signer pluginSigner, wantEr string, opts nota } } -func basicSignTest(t *testing.T, pluginSigner *pluginSigner, envelopeType string, metadata *proto.GetMetadataResponse) { +func basicSignTest(t *testing.T, pluginSigner *PluginSigner, envelopeType string, metadata *proto.GetMetadataResponse) { validSignOpts.SignatureMediaType = envelopeType data, signerInfo, err := pluginSigner.Sign(context.Background(), validSignDescriptor, validSignOpts) if err != nil { diff --git a/signer/signer.go b/signer/signer.go index cefc7788..54736af9 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -13,7 +13,7 @@ // Package signer provides notation signing functionality. It implements the // notation.Signer interface by providing builtinSigner for local signing and -// pluginSigner for remote signing. +// PluginSigner for remote signing. package signer import ( @@ -24,6 +24,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "time" "github.com/notaryproject/notation-core-go/signature" @@ -146,3 +147,22 @@ func (s *genericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts // TODO: re-enable timestamping https://github.com/notaryproject/notation-go/issues/78 return sig, &envContent.SignerInfo, nil } + +// SignBlob signs the artifact described by its descriptor and returns the +// marshalled envelope. +func (s *genericSigner) SignBlob(ctx context.Context, reader io.Reader, opts notation.SignBlobOptions) ([]byte, *signature.SignerInfo, error) { + logger := log.GetLogger(ctx) + logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType) + + ks, err := s.KeySpec() + if err != nil { + return nil, nil, err + } + + desc, err := getDescriptor(reader, opts.ContentMediaType, ks) + if err != nil { + return nil, nil, err + } + + return s.Sign(ctx, desc, opts.SignerSignOptions) +} From 7f990ba4e5f3bf73bbd041a803afe0b16d742315 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Sat, 27 Jan 2024 21:54:15 -0800 Subject: [PATCH 02/14] more fixes Signed-off-by: Pritesh Bandi --- signer/signer.go | 4 +-- signer/signer_test.go | 61 +++++++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/signer/signer.go b/signer/signer.go index 54736af9..a9743604 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -43,7 +43,7 @@ type genericSigner struct { } // New returns a builtinSigner given key and cert chain -func New(key crypto.PrivateKey, certChain []*x509.Certificate) (notation.Signer, error) { +func New(key crypto.PrivateKey, certChain []*x509.Certificate) (*genericSigner, error) { localSigner, err := signature.NewLocalSigner(certChain, key) if err != nil { return nil, err @@ -54,7 +54,7 @@ func New(key crypto.PrivateKey, certChain []*x509.Certificate) (notation.Signer, } // NewFromFiles returns a builtinSigner given key and certChain paths. -func NewFromFiles(keyPath, certChainPath string) (notation.Signer, error) { +func NewFromFiles(keyPath, certChainPath string) (*genericSigner, error) { if keyPath == "" { return nil, errors.New("key path not specified") } diff --git a/signer/signer_test.go b/signer/signer_test.go index c8d0ae38..75f823d6 100644 --- a/signer/signer_test.go +++ b/signer/signer_test.go @@ -27,6 +27,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "testing" "time" @@ -173,7 +174,47 @@ func TestSignWithCertChain(t *testing.T) { for _, envelopeType := range signature.RegisteredEnvelopeTypes() { for _, keyCert := range keyCertPairCollections { t.Run(fmt.Sprintf("envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { - validateSignWithCerts(t, envelopeType, keyCert.key, keyCert.certs) + s, err := New(keyCert.key, keyCert.certs) + if err != nil { + t.Fatalf("NewSigner() error = %v", err) + } + + desc, sOpts := generateSigningContent() + sOpts.SignatureMediaType = envelopeType + sig, _, err := s.Sign(context.Background(), desc, sOpts) + if err != nil { + t.Fatalf("Sign() error = %v", err) + } + + // basic verification + basicVerification(t, sig, envelopeType, keyCert.certs[len(keyCert.certs)-1], nil) + }) + } + } +} + +func TestSignBlobWithCertChain(t *testing.T) { + // sign with key + for _, envelopeType := range signature.RegisteredEnvelopeTypes() { + for _, keyCert := range keyCertPairCollections { + t.Run(fmt.Sprintf("envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { + s, err := New(keyCert.key, keyCert.certs) + if err != nil { + t.Fatalf("NewSigner() error = %v", err) + } + + sOpts := notation.SignBlobOptions{ + SignerSignOptions: notation.SignerSignOptions{ + SignatureMediaType: envelopeType, + }, + } + sig, _, err := s.SignBlob(context.Background(), strings.NewReader("some content"), sOpts) + if err != nil { + t.Fatalf("Sign() error = %v", err) + } + + // basic verification + basicVerification(t, sig, envelopeType, keyCert.certs[len(keyCert.certs)-1], nil) }) } } @@ -293,21 +334,3 @@ func verifySigningAgent(t *testing.T, signingAgentId string, metadata *proto.Get t.Fatalf("Expected signingAgent of %s %s/%s but signature contained %s instead", signingAgent, metadata.Name, metadata.Version, signingAgentId) } } - -func validateSignWithCerts(t *testing.T, envelopeType string, key crypto.PrivateKey, certs []*x509.Certificate) { - s, err := New(key, certs) - if err != nil { - t.Fatalf("NewSigner() error = %v", err) - } - - ctx := context.Background() - desc, sOpts := generateSigningContent() - sOpts.SignatureMediaType = envelopeType - sig, _, err := s.Sign(ctx, desc, sOpts) - if err != nil { - t.Fatalf("Sign() error = %v", err) - } - - // basic verification - basicVerification(t, sig, envelopeType, certs[len(certs)-1], nil) -} From f23b79400bdf3e0613e658fca2863541bccae5fd Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Tue, 30 Jan 2024 17:49:17 -0800 Subject: [PATCH 03/14] more fixes Signed-off-by: Pritesh Bandi --- signer/plugin_test.go | 45 +++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/signer/plugin_test.go b/signer/plugin_test.go index 41e51340..84576ee5 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -298,7 +298,30 @@ func TestPluginSigner_Sign_Valid(t *testing.T) { pluginSigner := PluginSigner{ plugin: newMockPlugin(keyCert.key, keyCert.certs, keySpec), } - basicSignTest(t, &pluginSigner, envelopeType, &validMetadata) + validSignOpts.SignatureMediaType = envelopeType + data, signerInfo, err := pluginSigner.Sign(context.Background(), validSignDescriptor, validSignOpts) + basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err) + }) + } + } +} + +func TestPluginSigner_SignBlob_Valid(t *testing.T) { + for _, envelopeType := range signature.RegisteredEnvelopeTypes() { + for _, keyCert := range keyCertPairCollections { + t.Run(fmt.Sprintf("external plugin,envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { + keySpec, _ := proto.DecodeKeySpec(proto.KeySpec(keyCert.keySpecName)) + pluginSigner := PluginSigner{ + plugin: newMockPlugin(keyCert.key, keyCert.certs, keySpec), + } + sOpts := notation.SignBlobOptions{ + SignerSignOptions: notation.SignerSignOptions{ + SignatureMediaType: envelopeType, + }, + } + + data, signerInfo, err := pluginSigner.SignBlob(context.Background(), strings.NewReader("some content"), sOpts) + basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err) }) } } @@ -329,7 +352,9 @@ func TestPluginSigner_SignEnvelope_Valid(t *testing.T) { pluginSigner := PluginSigner{ plugin: mockPlugin, } - basicSignTest(t, &pluginSigner, envelopeType, &validMetadata) + validSignOpts.SignatureMediaType = envelopeType + data, signerInfo, err := pluginSigner.Sign(context.Background(), validSignDescriptor, validSignOpts) + basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err) }) } } @@ -350,7 +375,9 @@ func TestPluginSigner_SignWithAnnotations_Valid(t *testing.T) { wantEnvelope: true, }, } - basicSignTest(t, &pluginSigner, envelopeType, &validMetadata) + validSignOpts.SignatureMediaType = envelopeType + data, signerInfo, err := pluginSigner.Sign(context.Background(), validSignDescriptor, validSignOpts) + basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err) if !reflect.DeepEqual(pluginSigner.PluginAnnotations(), annts) { fmt.Println(pluginSigner.PluginAnnotations()) t.Errorf("mismatch in annotations returned from PluginAnnotations()") @@ -368,9 +395,7 @@ func testSignerError(t *testing.T, signer PluginSigner, wantEr string, opts nota } } -func basicSignTest(t *testing.T, pluginSigner *PluginSigner, envelopeType string, metadata *proto.GetMetadataResponse) { - validSignOpts.SignatureMediaType = envelopeType - data, signerInfo, err := pluginSigner.Sign(context.Background(), validSignDescriptor, validSignOpts) +func basicSignTest(t *testing.T, ps *PluginSigner, envelopeType string, data []byte, signerInfo *signature.SignerInfo, err error) { if err != nil { t.Fatalf("Signer.Sign() error = %v, wantErr nil", err) } @@ -399,12 +424,12 @@ func basicSignTest(t *testing.T, pluginSigner *PluginSigner, envelopeType string TargetArtifact: validSignDescriptor, } if !reflect.DeepEqual(expectedPayload, gotPayload) { - t.Fatalf("Signer.Sign() descriptor subject changed, expect: %v, got: %v", expectedPayload, payload) + t.Fatalf("Signer.Sign() descriptor subject changed, expect: %+v, got: %+v", expectedPayload, payload) } if signerInfo.SignedAttributes.SigningScheme != signature.SigningSchemeX509 { - t.Fatalf("Signer.Sign() signing scheme changed, expect: %v, got: %v", signerInfo.SignedAttributes.SigningScheme, signature.SigningSchemeX509) + t.Fatalf("Signer.Sign() signing scheme changed, expect: %+v, got: %+v", signerInfo.SignedAttributes.SigningScheme, signature.SigningSchemeX509) } - mockPlugin := pluginSigner.plugin.(*mockPlugin) + mockPlugin := ps.plugin.(*mockPlugin) if mockPlugin.keySpec.SignatureAlgorithm() != signerInfo.SignatureAlgorithm { t.Fatalf("Signer.Sign() signing algorithm changed") } @@ -414,5 +439,5 @@ func basicSignTest(t *testing.T, pluginSigner *PluginSigner, envelopeType string if !reflect.DeepEqual(mockPlugin.certs, signerInfo.CertificateChain) { t.Fatalf(" Signer.Sign() cert chain changed") } - basicVerification(t, data, envelopeType, mockPlugin.certs[len(mockPlugin.certs)-1], metadata) + basicVerification(t, data, envelopeType, mockPlugin.certs[len(mockPlugin.certs)-1], &validMetadata) } From 46420b92d0450f5419f1a670cf5c0d9bf202518a Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Mon, 5 Feb 2024 17:48:16 -0800 Subject: [PATCH 04/14] fix issue where we were calculating digest twice Signed-off-by: Pritesh Bandi --- signer/plugin.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/signer/plugin.go b/signer/plugin.go index 21883e1b..e229275b 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -15,6 +15,7 @@ package signer import ( "context" + "crypto" "crypto/x509" "encoding/json" "errors" @@ -43,6 +44,12 @@ type PluginSigner struct { manifestAnnotations map[string]string } +var algorithms = map[crypto.Hash]digest.Algorithm{ + crypto.SHA256: digest.SHA256, + crypto.SHA384: digest.SHA384, + crypto.SHA512: digest.SHA512, +} + // NewFromPlugin creates a notation.Signer that signs artifacts and generates // signatures by delegating the one or more operations to the named plugin, // as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces. @@ -246,7 +253,7 @@ func getDescriptor(reader io.Reader, mediaType string, ks signature.KeySpec) (oc return ocispec.Descriptor{ MediaType: mediaType, - Digest: digest.FromBytes(hash), + Digest: digest.NewDigestFromBytes(algorithms[hashAlgo], hash), Size: bytes, }, nil } From 4bffed1009f153dc4c1a51d74cda48f95394d742 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Sat, 2 Mar 2024 19:49:44 -0800 Subject: [PATCH 05/14] Updating APIs Signed-off-by: Pritesh Bandi --- notation.go | 95 +++++++++++++++++++++++++++---------------- notation_test.go | 51 ++++++++++++++--------- signer/plugin.go | 64 +++++++++++------------------ signer/plugin_test.go | 47 ++++++++++++++++----- signer/signer.go | 27 ++++++------ signer/signer_test.go | 77 ++++++++++++++++++++++++++--------- 6 files changed, 223 insertions(+), 138 deletions(-) diff --git a/notation.go b/notation.go index 868d7d62..8b6ebb8b 100644 --- a/notation.go +++ b/notation.go @@ -30,6 +30,8 @@ import ( orasRegistry "oras.land/oras-go/v2/registry" "github.com/notaryproject/notation-core-go/signature" + "github.com/notaryproject/notation-core-go/signature/cose" + "github.com/notaryproject/notation-core-go/signature/jws" "github.com/notaryproject/notation-go/internal/envelope" "github.com/notaryproject/notation-go/log" "github.com/notaryproject/notation-go/registry" @@ -44,7 +46,7 @@ var reservedAnnotationPrefixes = [...]string{"io.cncf.notary"} // SignerSignOptions contains parameters for Signer.Sign. type SignerSignOptions struct { // SignatureMediaType is the envelope type of the signature. - // Currently both `application/jose+json` and `application/cose` are + // Currently, both `application/jose+json` and `application/cose` are // supported. SignatureMediaType string @@ -59,19 +61,34 @@ type SignerSignOptions struct { SigningAgent string } -// Signer is a generic interface for signing an artifact. +// Signer is a generic interface for signing an OCI artifact. // The interface allows signing with local or remote keys, // and packing in various signature formats. type Signer interface { - // Sign signs the artifact described by its descriptor, + // Sign signs the OCI artifact described by its descriptor, // and returns the signature and SignerInfo. Sign(ctx context.Context, desc ocispec.Descriptor, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error) } +// SignBlobOptions contains parameters for notation.SignBlob. +type SignBlobOptions struct { + SignerSignOptions + + ContentMediaType string + // UserMetadata contains key-value pairs that are added to the signature + // payload + UserMetadata map[string]string +} + +type BlobDescriptorGenerator func(digest.Algorithm) (ocispec.Descriptor, error) + +// BlobSigner is a generic interface for signing arbitrary data. +// The interface allows signing with local or remote keys, +// and packing in various signature formats. type BlobSigner interface { - // SignBlob signs the artifact described by its descriptor, - // and returns the signature and SignerInfo. - SignBlob(ctx context.Context, reader io.Reader, opts SignBlobOptions) ([]byte, *signature.SignerInfo, error) + // SignBlob signs the descriptor returned by genDesc , + // and returns the signature and SignerInfo + SignBlob(ctx context.Context, genDesc BlobDescriptorGenerator, opts SignerSignOptions) ([]byte, *signature.SignerInfo, error) } // signerAnnotation facilitates return of manifest annotations by signers @@ -94,20 +111,10 @@ type SignOptions struct { UserMetadata map[string]string } -type SignBlobOptions struct { - SignerSignOptions - - ContentMediaType string - - // UserMetadata contains key-value pairs that are added to the signature - // payload - UserMetadata map[string]string -} - -// Sign signs the artifact and push the signature to the Repository. +// Sign signs the OCI artifact and push the signature to the Repository. // The descriptor of the sign content is returned upon successful signing. func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts SignOptions) (ocispec.Descriptor, error) { - // sanity checks + // sanity check if err := validate(signer, signOpts.SignerSignOptions); err != nil { return ocispec.Descriptor{}, err } @@ -165,26 +172,41 @@ func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts return targetDesc, nil } -func SignBlob(ctx context.Context, signer BlobSigner, reader io.Reader, signOpts SignBlobOptions) ([]byte, error) { +// SignBlob signs the arbitrary data and returns the signature +func SignBlob(ctx context.Context, signer BlobSigner, reader io.Reader, signBlobOpts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { // sanity checks - if err := validate(signer, signOpts.SignerSignOptions); err != nil { - return nil, err + if err := validate(signer, signBlobOpts.SignerSignOptions); err != nil { + return nil, nil, err } + if reader == nil { - return nil, errors.New("reader cannot be nil") + return nil, nil, errors.New("reader cannot be nil") } - if signOpts.ContentMediaType != "" { - if _, _, err := mime.ParseMediaType(signOpts.ContentMediaType); err != nil { - return nil, fmt.Errorf("invalid content media-type '%s': %v", signOpts.ContentMediaType, err) - } + + if signBlobOpts.ContentMediaType == "" { + return nil, nil, errors.New("content media-type cannot be empty") } - sig, _, err := signer.SignBlob(ctx, reader, signOpts) - if err != nil { - return nil, err + if _, _, err := mime.ParseMediaType(signBlobOpts.ContentMediaType); err != nil { + return nil, nil, fmt.Errorf("invalid content media-type '%s': %v", signBlobOpts.ContentMediaType, err) + } + + getDescFun := func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { + h := hashAlgo.Hash() + bytes, err := io.Copy(hashAlgo.Hash(), reader) + if err != nil { + return ocispec.Descriptor{}, err + } + + targetDesc := ocispec.Descriptor{ + MediaType: signBlobOpts.ContentMediaType, + Digest: digest.NewDigest(hashAlgo, h), + Size: bytes, + } + return addUserMetadataToDescriptor(ctx, targetDesc, signBlobOpts.UserMetadata) } - return sig, nil + return signer.SignBlob(ctx, getDescFun, signBlobOpts.SignerSignOptions) } func validate(signer any, signOpts SignerSignOptions) error { @@ -192,14 +214,19 @@ func validate(signer any, signOpts SignerSignOptions) error { return errors.New("signer cannot be nil") } if signOpts.ExpiryDuration < 0 { - return fmt.Errorf("expiry duration cannot be a negative value") + return errors.New("expiry duration cannot be a negative value") } if signOpts.ExpiryDuration%time.Second != 0 { - return fmt.Errorf("expiry duration supports minimum granularity of seconds") + return errors.New("expiry duration supports minimum granularity of seconds") } - if _, _, err := mime.ParseMediaType(signOpts.SignatureMediaType); err != nil { - return fmt.Errorf("invalid signature media-type '%s': %v", signOpts.SignatureMediaType, err) + if signOpts.SignatureMediaType == "" { + return errors.New("signature media-type cannot be empty") } + + if !(signOpts.SignatureMediaType == jws.MediaTypeEnvelope || signOpts.SignatureMediaType == cose.MediaTypeEnvelope) { + return fmt.Errorf("invalid signature media-type '%s'", signOpts.SignatureMediaType) + } + return nil } diff --git a/notation_test.go b/notation_test.go index f63971c4..4c1736b8 100644 --- a/notation_test.go +++ b/notation_test.go @@ -32,6 +32,7 @@ import ( "github.com/notaryproject/notation-go/plugin" "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation-go/verifier/trustpolicy" + "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -75,9 +76,7 @@ func TestSignBlobSuccess(t *testing.T) { {"expiryInHours", 24 * time.Hour, "video/mp4", "", nil, nil}, {"oneSecondExpiry", 1 * time.Second, "video/mp4", "", nil, nil}, {"zeroExpiry", 0, "video/mp4", "", nil, nil}, - //{"invalidContentMediaType", 1 * time.Second, "zap/zop/sop", nil, nil}, {"validContentType", 1 * time.Second, "video/mp4", "", nil, nil}, - {"emptyContentType", 1 * time.Second, "", "", nil, nil}, {"emptyContentType", 1 * time.Second, "video/mp4", "someDummyAgent", map[string]string{"hi": "hello"}, map[string]string{"bye": "tata"}}, } for _, tc := range testCases { @@ -93,7 +92,7 @@ func TestSignBlobSuccess(t *testing.T) { ContentMediaType: tc.mtype, } - _, err := SignBlob(context.Background(), &dummySigner{}, reader, opts) + _, _, err := SignBlob(context.Background(), &dummySigner{}, reader, opts) if err != nil { b.Fatalf("Sign failed with error: %v", err) } @@ -101,36 +100,43 @@ func TestSignBlobSuccess(t *testing.T) { } } -func TestSignError(t *testing.T) { +func TestSignBlobError(t *testing.T) { reader := strings.NewReader("some content") testCases := []struct { - name string - signer BlobSigner - dur time.Duration - rdr io.Reader - mtype string + name string + signer BlobSigner + dur time.Duration + rdr io.Reader + sigMType string + ctMType string + errMsg string }{ - {"negativeExpiry", &dummySigner{}, -1 * time.Second, nil, "video/mp4"}, - {"milliSecExpiry", &dummySigner{}, 1 * time.Millisecond, nil, "video/mp4"}, - {"invalidContentMediaType", &dummySigner{}, 1 * time.Second, reader, "video/mp4/zoping"}, - {"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4"}, - {"nilSigner", nil, 1 * time.Second, reader, "video/mp4"}, - {"signerError", &dummySigner{fail: true}, 1 * time.Second, reader, "video/mp4"}, + {"negativeExpiry", &dummySigner{}, -1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "expiry duration cannot be a negative value"}, + {"milliSecExpiry", &dummySigner{}, 1 * time.Millisecond, nil, "video/mp4", jws.MediaTypeEnvelope, "expiry duration supports minimum granularity of seconds"}, + {"invalidContentMediaType", &dummySigner{}, 1 * time.Second, reader, "video/mp4/zoping", jws.MediaTypeEnvelope, "invalid content media-type 'video/mp4/zoping': mime: unexpected content after media subtype"}, + {"emptyContentMediaType", &dummySigner{}, 1 * time.Second, reader, "", jws.MediaTypeEnvelope, "content media-type cannot be empty"}, + {"invalidSignatureMediaType", &dummySigner{}, 1 * time.Second, reader, "", "", "content media-type cannot be empty"}, + {"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "reader cannot be nil"}, + {"nilSigner", nil, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "signer cannot be nil"}, + {"signerError", &dummySigner{fail: true}, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "expected SignBlob failure"}, } for _, tc := range testCases { - t.Run(tc.name, func(b *testing.T) { + t.Run(tc.name, func(t *testing.T) { opts := SignBlobOptions{ SignerSignOptions: SignerSignOptions{ SignatureMediaType: jws.MediaTypeEnvelope, ExpiryDuration: tc.dur, PluginConfig: nil, }, - ContentMediaType: tc.mtype, + ContentMediaType: tc.sigMType, } - _, err := SignBlob(context.Background(), tc.signer, tc.rdr, opts) + _, _, err := SignBlob(context.Background(), tc.signer, tc.rdr, opts) if err == nil { - b.Error("expected error but didnt found") + t.Fatalf("expected error but didnt found") + } + if err.Error() != tc.errMsg { + t.Fatalf("expected err message to be '%s' but found '%s'", tc.errMsg, err.Error()) } }) } @@ -414,11 +420,16 @@ func (s *dummySigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts Si }, nil } -func (s *dummySigner) SignBlob(ctx context.Context, reader io.Reader, opts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { +func (s *dummySigner) SignBlob(_ context.Context, descGenFunc BlobDescriptorGenerator, _ SignerSignOptions) ([]byte, *signature.SignerInfo, error) { if s.fail { return nil, nil, errors.New("expected SignBlob failure") } + _, err := descGenFunc(digest.SHA384) + if err != nil { + return nil, nil, err + } + return []byte("ABC"), &signature.SignerInfo{ SignedAttributes: signature.SignedAttributes{ SigningTime: time.Now(), diff --git a/signer/plugin.go b/signer/plugin.go index e229275b..4d51d4ad 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -20,7 +20,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "time" "oras.land/oras-go/v2/content" @@ -80,14 +79,14 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n mergedConfig := s.mergeConfig(opts.PluginConfig) logger.Debug("Invoking plugin's get-plugin-metadata command") - metadata, err := s.plugin.GetMetadata(ctx, &proto.GetMetadataRequest{PluginConfig: mergedConfig}) + metadata, err := s.plugin.GetMetadata(ctx, &plugin.GetMetadataRequest{PluginConfig: mergedConfig}) if err != nil { return nil, nil, err } - logger.Debugf("Using plugin %v with capabilities %v to sign artifact %v in signature media type %v", metadata.Name, metadata.Capabilities, desc.Digest, opts.SignatureMediaType) + logger.Debugf("Using plugin %v with capabilities %v to sign oci artifact %v in signature media type %v", metadata.Name, metadata.Capabilities, desc.Digest, opts.SignatureMediaType) if metadata.HasCapability(proto.CapabilitySignatureGenerator) { - // Get key info and validate. + logger.Debug("Invoking plugin's describe-key command") ks, err := s.getKeySpec(ctx, mergedConfig) if err != nil { return nil, nil, err @@ -100,40 +99,43 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n } // SignBlob signs the arbitrary data and returns the marshalled envelope. -func (s *PluginSigner) SignBlob(ctx context.Context, reader io.Reader, opts notation.SignBlobOptions) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) mergedConfig := s.mergeConfig(opts.PluginConfig) logger.Debug("Invoking plugin's get-plugin-metadata command") - metadata, err := s.plugin.GetMetadata(ctx, &proto.GetMetadataRequest{PluginConfig: mergedConfig}) + metadata, err := s.plugin.GetMetadata(ctx, &plugin.GetMetadataRequest{PluginConfig: mergedConfig}) if err != nil { return nil, nil, err } - // Get key info and validate. + logger.Debug("Invoking plugin's describe-key command") ks, err := s.getKeySpec(ctx, mergedConfig) if err != nil { return nil, nil, err } - desc, err := getDescriptor(reader, opts.ContentMediaType, ks) + digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] + if !ok { + return nil, nil, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm()) + } + + // get descriptor to sign + desc, err := blobGenFunc(digestAlg) if err != nil { return nil, nil, err } - logger.Debugf("Using plugin %v with capabilities %v to sign artifact using descriptor %+v", metadata.Name, metadata.Capabilities, desc) + logger.Debugf("Using plugin %v with capabilities %v to sign blob using descriptor %+v", metadata.Name, metadata.Capabilities, desc) if metadata.HasCapability(proto.CapabilitySignatureGenerator) { - return s.generateSignature(ctx, desc, opts.SignerSignOptions, ks, metadata) + return s.generateSignature(ctx, desc, opts, ks, metadata) } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { - return s.generateSignatureEnvelope(ctx, desc, opts.SignerSignOptions) + return s.generateSignatureEnvelope(ctx, desc, opts) } return nil, nil, fmt.Errorf("plugin does not have signing capabilities") } func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) (signature.KeySpec, error) { - // Get key info and validate. - logger := log.GetLogger(ctx) - logger.Debug("Invoking plugin's describe-key command") descKeyResp, err := s.describeKey(ctx, config) if err != nil { return signature.KeySpec{}, err @@ -146,10 +148,10 @@ func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) return proto.DecodeKeySpec(descKeyResp.KeySpec) } -func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, ks signature.KeySpec, metadata *proto.GetMetadataResponse) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, ks signature.KeySpec, metadata *plugin.GetMetadataResponse) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debug("Generating signature by plugin") - genericSigner := genericSigner{ + genericSigner := GenericSigner{ Signer: &pluginPrimitiveSigner{ ctx: ctx, plugin: s.plugin, @@ -207,9 +209,9 @@ func (s *PluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocisp return nil, nil, err } - content := envContent.Payload.Content + cnt := envContent.Payload.Content var signedPayload envelope.Payload - if err = json.Unmarshal(content, &signedPayload); err != nil { + if err = json.Unmarshal(cnt, &signedPayload); err != nil { return nil, nil, fmt.Errorf("signed envelope payload can't be unmarshalled: %w", err) } @@ -217,7 +219,7 @@ func (s *PluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocisp return nil, nil, fmt.Errorf("during signing descriptor subject has changed from %+v to %+v", desc, signedPayload.TargetArtifact) } - if unknownAttributes := areUnknownAttributesAdded(content); len(unknownAttributes) != 0 { + if unknownAttributes := areUnknownAttributesAdded(cnt); len(unknownAttributes) != 0 { return nil, nil, fmt.Errorf("during signing, following unknown attributes were added to subject descriptor: %+q", unknownAttributes) } @@ -238,28 +240,8 @@ func (s *PluginSigner) mergeConfig(config map[string]string) map[string]string { return c } -func getDescriptor(reader io.Reader, mediaType string, ks signature.KeySpec) (ocispec.Descriptor, error) { - hashAlgo := ks.SignatureAlgorithm().Hash() - h := hashAlgo.HashFunc().New() - bytes, err := io.Copy(h, reader) - if err != nil { - return ocispec.Descriptor{}, err - } - - hash := h.Sum(nil) - if mediaType == "" { - mediaType = "application/octet-stream" - } - - return ocispec.Descriptor{ - MediaType: mediaType, - Digest: digest.NewDigestFromBytes(algorithms[hashAlgo], hash), - Size: bytes, - }, nil -} - -func (s *PluginSigner) describeKey(ctx context.Context, config map[string]string) (*proto.DescribeKeyResponse, error) { - req := &proto.DescribeKeyRequest{ +func (s *PluginSigner) describeKey(ctx context.Context, config map[string]string) (*plugin.DescribeKeyResponse, error) { + req := &plugin.DescribeKeyRequest{ KeyID: s.keyID, PluginConfig: config, } diff --git a/signer/plugin_test.go b/signer/plugin_test.go index 84576ee5..634df66f 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -32,6 +32,7 @@ import ( "github.com/notaryproject/notation-go/internal/envelope" "github.com/notaryproject/notation-go/plugin" "github.com/notaryproject/notation-go/plugin/proto" + "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -70,6 +71,16 @@ type mockPlugin struct { keySpec signature.KeySpec } +func getDescriptorFunc(throwError bool) func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { + return func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { + if throwError { + return ocispec.Descriptor{}, errors.New("") + } + return validSignDescriptor, nil + } + +} + func newMockPlugin(key crypto.PrivateKey, certs []*x509.Certificate, keySpec signature.KeySpec) *mockPlugin { return &mockPlugin{ key: key, @@ -206,10 +217,29 @@ func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *proto.GenerateEn } func TestNewFromPluginFailed(t *testing.T) { - wantErr := "keyID not specified" - _, err := NewFromPlugin(&plugin.CLIPlugin{}, "", make(map[string]string)) - if err == nil || err.Error() != wantErr { - t.Fatalf("TestNewFromPluginFailed expects error %q, got %q", wantErr, err.Error()) + tests := map[string]struct { + pl plugin.SignPlugin + keyID string + errMsg string + }{ + "Invalid KeyID": { + pl: &plugin.CLIPlugin{}, + keyID: "", + errMsg: "keyID not specified", + }, + "nilPlugin": { + pl: nil, + keyID: "someKeyId", + errMsg: "nil plugin", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + _, err := NewFromPlugin(tc.pl, tc.keyID, make(map[string]string)) + if err == nil || err.Error() != tc.errMsg { + t.Fatalf("TestNewFromPluginFailed expects error %q, got %q", tc.errMsg, err.Error()) + } + }) } } @@ -314,13 +344,8 @@ func TestPluginSigner_SignBlob_Valid(t *testing.T) { pluginSigner := PluginSigner{ plugin: newMockPlugin(keyCert.key, keyCert.certs, keySpec), } - sOpts := notation.SignBlobOptions{ - SignerSignOptions: notation.SignerSignOptions{ - SignatureMediaType: envelopeType, - }, - } - - data, signerInfo, err := pluginSigner.SignBlob(context.Background(), strings.NewReader("some content"), sOpts) + validSignOpts.SignatureMediaType = envelopeType + data, signerInfo, err := pluginSigner.SignBlob(context.Background(), getDescriptorFunc(false), validSignOpts) basicSignTest(t, &pluginSigner, envelopeType, data, signerInfo, err) }) } diff --git a/signer/signer.go b/signer/signer.go index a9743604..55ae2585 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -24,7 +24,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "time" "github.com/notaryproject/notation-core-go/signature" @@ -37,24 +36,24 @@ import ( // signingAgent is the unprotected header field used by signature. const signingAgent = "Notation/1.0.0" -// genericSigner implements notation.Signer and embeds signature.Signer -type genericSigner struct { +// GenericSigner implements notation.Signer and embeds signature.Signer +type GenericSigner struct { signature.Signer } // New returns a builtinSigner given key and cert chain -func New(key crypto.PrivateKey, certChain []*x509.Certificate) (*genericSigner, error) { +func New(key crypto.PrivateKey, certChain []*x509.Certificate) (*GenericSigner, error) { localSigner, err := signature.NewLocalSigner(certChain, key) if err != nil { return nil, err } - return &genericSigner{ + return &GenericSigner{ Signer: localSigner, }, nil } // NewFromFiles returns a builtinSigner given key and certChain paths. -func NewFromFiles(keyPath, certChainPath string) (*genericSigner, error) { +func NewFromFiles(keyPath, certChainPath string) (*GenericSigner, error) { if keyPath == "" { return nil, errors.New("key path not specified") } @@ -86,7 +85,7 @@ func NewFromFiles(keyPath, certChainPath string) (*genericSigner, error) { // Sign signs the artifact described by its descriptor and returns the // marshalled envelope. -func (s *genericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *GenericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debugf("Generic signing for %v in signature media type %v", desc.Digest, opts.SignatureMediaType) // Generate payload to be signed. @@ -148,9 +147,8 @@ func (s *genericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts return sig, &envContent.SignerInfo, nil } -// SignBlob signs the artifact described by its descriptor and returns the -// marshalled envelope. -func (s *genericSigner) SignBlob(ctx context.Context, reader io.Reader, opts notation.SignBlobOptions) ([]byte, *signature.SignerInfo, error) { +// SignBlob signs the descriptor returned by blobGen and returns the marshalled envelope +func (s *GenericSigner) SignBlob(ctx context.Context, blobGen notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType) @@ -159,10 +157,15 @@ func (s *genericSigner) SignBlob(ctx context.Context, reader io.Reader, opts not return nil, nil, err } - desc, err := getDescriptor(reader, opts.ContentMediaType, ks) + digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] + if !ok { + return nil, nil, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm().Hash()) + } + + desc, err := blobGen(digestAlg) if err != nil { return nil, nil, err } - return s.Sign(ctx, desc, opts.SignerSignOptions) + return s.Sign(ctx, desc, opts) } diff --git a/signer/signer_test.go b/signer/signer_test.go index 75f823d6..0124da2b 100644 --- a/signer/signer_test.go +++ b/signer/signer_test.go @@ -27,7 +27,6 @@ import ( "os" "path/filepath" "regexp" - "strings" "testing" "time" @@ -169,25 +168,47 @@ func TestNewFromFiles(t *testing.T) { } } +func TestNewFromFilesError(t *testing.T) { + tests := map[string]struct { + keyPath string + certPath string + errMsg string + }{ + "empty key path": { + keyPath: "", + certPath: "someCert", + errMsg: "key path not specified", + }, + "empty cert path": { + keyPath: "someKeyId", + certPath: "", + errMsg: "certificate path not specified", + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + _, err := NewFromFiles(tc.keyPath, tc.certPath) + if err == nil || err.Error() != tc.errMsg { + t.Fatalf("TestNewFromPluginFailed expects error %q, got %q", tc.errMsg, err.Error()) + } + }) + } +} + +func TestNewError(t *testing.T) { + wantErr := "\"certs\" param is invalid. Error: empty certs" + _, err := New(nil, nil) + if err == nil || err.Error() != wantErr { + t.Fatalf("TestNewFromPluginFailed expects error %q, got %q", wantErr, err.Error()) + } +} + func TestSignWithCertChain(t *testing.T) { // sign with key for _, envelopeType := range signature.RegisteredEnvelopeTypes() { for _, keyCert := range keyCertPairCollections { t.Run(fmt.Sprintf("envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { - s, err := New(keyCert.key, keyCert.certs) - if err != nil { - t.Fatalf("NewSigner() error = %v", err) - } - - desc, sOpts := generateSigningContent() - sOpts.SignatureMediaType = envelopeType - sig, _, err := s.Sign(context.Background(), desc, sOpts) - if err != nil { - t.Fatalf("Sign() error = %v", err) - } - - // basic verification - basicVerification(t, sig, envelopeType, keyCert.certs[len(keyCert.certs)-1], nil) + validateSignWithCerts(t, envelopeType, keyCert.key, keyCert.certs) }) } } @@ -203,12 +224,10 @@ func TestSignBlobWithCertChain(t *testing.T) { t.Fatalf("NewSigner() error = %v", err) } - sOpts := notation.SignBlobOptions{ - SignerSignOptions: notation.SignerSignOptions{ - SignatureMediaType: envelopeType, - }, + sOpts := notation.SignerSignOptions{ + SignatureMediaType: envelopeType, } - sig, _, err := s.SignBlob(context.Background(), strings.NewReader("some content"), sOpts) + sig, _, err := s.SignBlob(context.Background(), getDescriptorFunc(false), sOpts) if err != nil { t.Fatalf("Sign() error = %v", err) } @@ -334,3 +353,21 @@ func verifySigningAgent(t *testing.T, signingAgentId string, metadata *proto.Get t.Fatalf("Expected signingAgent of %s %s/%s but signature contained %s instead", signingAgent, metadata.Name, metadata.Version, signingAgentId) } } + +func validateSignWithCerts(t *testing.T, envelopeType string, key crypto.PrivateKey, certs []*x509.Certificate) { + s, err := New(key, certs) + if err != nil { + t.Fatalf("NewSigner() error = %v", err) + } + + ctx := context.Background() + desc, sOpts := generateSigningContent() + sOpts.SignatureMediaType = envelopeType + sig, _, err := s.Sign(ctx, desc, sOpts) + if err != nil { + t.Fatalf("Sign() error = %v", err) + } + + // basic verification + basicVerification(t, sig, envelopeType, certs[len(certs)-1], nil) +} From ec5ea2f111f56a8b3de5a997734f1daf1f4bf575 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Mon, 4 Mar 2024 22:28:08 -0800 Subject: [PATCH 06/14] renaming variable Signed-off-by: Pritesh Bandi --- notation.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/notation.go b/notation.go index 8b6ebb8b..3a990ba7 100644 --- a/notation.go +++ b/notation.go @@ -173,14 +173,14 @@ func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts } // SignBlob signs the arbitrary data and returns the signature -func SignBlob(ctx context.Context, signer BlobSigner, reader io.Reader, signBlobOpts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { +func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, signBlobOpts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { // sanity checks if err := validate(signer, signBlobOpts.SignerSignOptions); err != nil { return nil, nil, err } - if reader == nil { - return nil, nil, errors.New("reader cannot be nil") + if blobReader == nil { + return nil, nil, errors.New("blobReader cannot be nil") } if signBlobOpts.ContentMediaType == "" { @@ -193,7 +193,7 @@ func SignBlob(ctx context.Context, signer BlobSigner, reader io.Reader, signBlob getDescFun := func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { h := hashAlgo.Hash() - bytes, err := io.Copy(hashAlgo.Hash(), reader) + bytes, err := io.Copy(hashAlgo.Hash(), blobReader) if err != nil { return ocispec.Descriptor{}, err } From aaa9f16d67c37f677624260c3f8ca2c2dbf6d921 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Mon, 4 Mar 2024 22:32:46 -0800 Subject: [PATCH 07/14] fix test Signed-off-by: Pritesh Bandi --- notation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notation_test.go b/notation_test.go index 4c1736b8..72255f5c 100644 --- a/notation_test.go +++ b/notation_test.go @@ -116,7 +116,7 @@ func TestSignBlobError(t *testing.T) { {"invalidContentMediaType", &dummySigner{}, 1 * time.Second, reader, "video/mp4/zoping", jws.MediaTypeEnvelope, "invalid content media-type 'video/mp4/zoping': mime: unexpected content after media subtype"}, {"emptyContentMediaType", &dummySigner{}, 1 * time.Second, reader, "", jws.MediaTypeEnvelope, "content media-type cannot be empty"}, {"invalidSignatureMediaType", &dummySigner{}, 1 * time.Second, reader, "", "", "content media-type cannot be empty"}, - {"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "reader cannot be nil"}, + {"nilReader", &dummySigner{}, 1 * time.Second, nil, "video/mp4", jws.MediaTypeEnvelope, "blobReader cannot be nil"}, {"nilSigner", nil, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "signer cannot be nil"}, {"signerError", &dummySigner{fail: true}, 1 * time.Second, reader, "video/mp4", jws.MediaTypeEnvelope, "expected SignBlob failure"}, } From 602cc5c2d31216eba7c14161ea438d0d4993a5c3 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Tue, 5 Mar 2024 12:22:17 -0800 Subject: [PATCH 08/14] pr Signed-off-by: Pritesh Bandi --- notation.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/notation.go b/notation.go index 3a990ba7..c1c1c04e 100644 --- a/notation.go +++ b/notation.go @@ -73,13 +73,14 @@ type Signer interface { // SignBlobOptions contains parameters for notation.SignBlob. type SignBlobOptions struct { SignerSignOptions - + // ContentMediaType is the media-type of the blob being signed. ContentMediaType string // UserMetadata contains key-value pairs that are added to the signature // payload UserMetadata map[string]string } +// BlobDescriptorGenerator creates descriptor using the digest Algorithm. type BlobDescriptorGenerator func(digest.Algorithm) (ocispec.Descriptor, error) // BlobSigner is a generic interface for signing arbitrary data. @@ -115,7 +116,7 @@ type SignOptions struct { // The descriptor of the sign content is returned upon successful signing. func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts SignOptions) (ocispec.Descriptor, error) { // sanity check - if err := validate(signer, signOpts.SignerSignOptions); err != nil { + if err := validateSignArguments(signer, signOpts.SignerSignOptions); err != nil { return ocispec.Descriptor{}, err } if repo == nil { @@ -175,7 +176,7 @@ func Sign(ctx context.Context, signer Signer, repo registry.Repository, signOpts // SignBlob signs the arbitrary data and returns the signature func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, signBlobOpts SignBlobOptions) ([]byte, *signature.SignerInfo, error) { // sanity checks - if err := validate(signer, signBlobOpts.SignerSignOptions); err != nil { + if err := validateSignArguments(signer, signBlobOpts.SignerSignOptions); err != nil { return nil, nil, err } @@ -191,7 +192,7 @@ func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, sign return nil, nil, fmt.Errorf("invalid content media-type '%s': %v", signBlobOpts.ContentMediaType, err) } - getDescFun := func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { + getDescFunc := func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { h := hashAlgo.Hash() bytes, err := io.Copy(hashAlgo.Hash(), blobReader) if err != nil { @@ -206,10 +207,10 @@ func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, sign return addUserMetadataToDescriptor(ctx, targetDesc, signBlobOpts.UserMetadata) } - return signer.SignBlob(ctx, getDescFun, signBlobOpts.SignerSignOptions) + return signer.SignBlob(ctx, getDescFunc, signBlobOpts.SignerSignOptions) } -func validate(signer any, signOpts SignerSignOptions) error { +func validateSignArguments(signer any, signOpts SignerSignOptions) error { if signer == nil { return errors.New("signer cannot be nil") } From 88d72691219e92e26598024e44ba33d12bd5311f Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Tue, 5 Mar 2024 12:51:09 -0800 Subject: [PATCH 09/14] pr Signed-off-by: Pritesh Bandi --- signer/plugin.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index 4d51d4ad..9b9bbda8 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -86,12 +86,11 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n logger.Debugf("Using plugin %v with capabilities %v to sign oci artifact %v in signature media type %v", metadata.Name, metadata.Capabilities, desc.Digest, opts.SignatureMediaType) if metadata.HasCapability(proto.CapabilitySignatureGenerator) { - logger.Debug("Invoking plugin's describe-key command") ks, err := s.getKeySpec(ctx, mergedConfig) if err != nil { return nil, nil, err } - return s.generateSignature(ctx, desc, opts, ks, metadata) + return s.generateSignature(ctx, desc, opts, ks, metadata, mergedConfig) } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { return s.generateSignatureEnvelope(ctx, desc, opts) } @@ -128,7 +127,7 @@ func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDe logger.Debugf("Using plugin %v with capabilities %v to sign blob using descriptor %+v", metadata.Name, metadata.Capabilities, desc) if metadata.HasCapability(proto.CapabilitySignatureGenerator) { - return s.generateSignature(ctx, desc, opts, ks, metadata) + return s.generateSignature(ctx, desc, opts, ks, metadata, mergedConfig) } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { return s.generateSignatureEnvelope(ctx, desc, opts) } @@ -136,6 +135,8 @@ func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDe } func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) (signature.KeySpec, error) { + logger := log.GetLogger(ctx) + logger.Debug("Invoking plugin's describe-key command") descKeyResp, err := s.describeKey(ctx, config) if err != nil { return signature.KeySpec{}, err @@ -148,7 +149,7 @@ func (s *PluginSigner) getKeySpec(ctx context.Context, config map[string]string) return proto.DecodeKeySpec(descKeyResp.KeySpec) } -func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, ks signature.KeySpec, metadata *plugin.GetMetadataResponse) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descriptor, opts notation.SignerSignOptions, ks signature.KeySpec, metadata *plugin.GetMetadataResponse, pluginConfig map[string]string) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debug("Generating signature by plugin") genericSigner := GenericSigner{ @@ -156,7 +157,7 @@ func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descr ctx: ctx, plugin: s.plugin, keyID: s.keyID, - pluginConfig: s.mergeConfig(opts.PluginConfig), + pluginConfig: pluginConfig, keySpec: ks, }, } @@ -209,9 +210,9 @@ func (s *PluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocisp return nil, nil, err } - cnt := envContent.Payload.Content + content := envContent.Payload.Content var signedPayload envelope.Payload - if err = json.Unmarshal(cnt, &signedPayload); err != nil { + if err = json.Unmarshal(content, &signedPayload); err != nil { return nil, nil, fmt.Errorf("signed envelope payload can't be unmarshalled: %w", err) } @@ -219,7 +220,7 @@ func (s *PluginSigner) generateSignatureEnvelope(ctx context.Context, desc ocisp return nil, nil, fmt.Errorf("during signing descriptor subject has changed from %+v to %+v", desc, signedPayload.TargetArtifact) } - if unknownAttributes := areUnknownAttributesAdded(cnt); len(unknownAttributes) != 0 { + if unknownAttributes := areUnknownAttributesAdded(content); len(unknownAttributes) != 0 { return nil, nil, fmt.Errorf("during signing, following unknown attributes were added to subject descriptor: %+q", unknownAttributes) } From 0b7be6c5022f058cf16fbd17ca7a7e48bc8c8983 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Mon, 11 Mar 2024 12:21:50 -0700 Subject: [PATCH 10/14] fixes Signed-off-by: Pritesh Bandi --- notation.go | 36 +++++++++++++++++++----------------- signer/plugin.go | 9 ++++++++- signer/signer.go | 16 +++++++++++++--- signer/signer_test.go | 2 +- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/notation.go b/notation.go index c1c1c04e..bb8cdaf6 100644 --- a/notation.go +++ b/notation.go @@ -192,21 +192,7 @@ func SignBlob(ctx context.Context, signer BlobSigner, blobReader io.Reader, sign return nil, nil, fmt.Errorf("invalid content media-type '%s': %v", signBlobOpts.ContentMediaType, err) } - getDescFunc := func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { - h := hashAlgo.Hash() - bytes, err := io.Copy(hashAlgo.Hash(), blobReader) - if err != nil { - return ocispec.Descriptor{}, err - } - - targetDesc := ocispec.Descriptor{ - MediaType: signBlobOpts.ContentMediaType, - Digest: digest.NewDigest(hashAlgo, h), - Size: bytes, - } - return addUserMetadataToDescriptor(ctx, targetDesc, signBlobOpts.UserMetadata) - } - + getDescFunc := getDescriptorFunc(ctx, blobReader, signBlobOpts.ContentMediaType, signBlobOpts.UserMetadata) return signer.SignBlob(ctx, getDescFunc, signBlobOpts.SignerSignOptions) } @@ -315,7 +301,7 @@ func (outcome *VerificationOutcome) UserMetadata() (map[string]string, error) { // VerifierVerifyOptions contains parameters for Verifier.Verify. type VerifierVerifyOptions struct { - // ArtifactReference is the reference of the artifact that is been + // ArtifactReference is the reference of the artifact that is being // verified against to. It must be a full reference. ArtifactReference string @@ -349,7 +335,7 @@ type verifySkipper interface { // VerifyOptions contains parameters for notation.Verify. type VerifyOptions struct { - // ArtifactReference is the reference of the artifact that is been + // ArtifactReference is the reference of the artifact that is being // verified against to. ArtifactReference string @@ -528,3 +514,19 @@ func generateAnnotations(signerInfo *signature.SignerInfo, annotations map[strin annotations[ocispec.AnnotationCreated] = signingTime.Format(time.RFC3339) return annotations, nil } + +func getDescriptorFunc(ctx context.Context, reader io.Reader, contentMediaType string, userMetadata map[string]string) BlobDescriptorGenerator { + return func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { + h := hashAlgo.Hash() + bytes, err := io.Copy(hashAlgo.Hash(), reader) + if err != nil { + return ocispec.Descriptor{}, err + } + targetDesc := ocispec.Descriptor{ + MediaType: contentMediaType, + Digest: digest.NewDigest(hashAlgo, h), + Size: bytes, + } + return addUserMetadataToDescriptor(ctx, targetDesc, userMetadata) + } +} diff --git a/signer/plugin.go b/signer/plugin.go index 9b9bbda8..f546a913 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -52,7 +52,14 @@ var algorithms = map[crypto.Hash]digest.Algorithm{ // NewFromPlugin creates a notation.Signer that signs artifacts and generates // signatures by delegating the one or more operations to the named plugin, // as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces. -func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (*PluginSigner, error) { +func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (notation.Signer, error) { + return NewPluginSigner(plugin, keyID, pluginConfig) +} + +// NewPluginSigner creates a notation.Signer that signs artifacts and generates +// signatures by delegating the one or more operations to the named plugin, +// as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces. +func NewPluginSigner(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (*PluginSigner, error) { if plugin == nil { return nil, errors.New("nil plugin") } diff --git a/signer/signer.go b/signer/signer.go index 55ae2585..56de756b 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -42,7 +42,12 @@ type GenericSigner struct { } // New returns a builtinSigner given key and cert chain -func New(key crypto.PrivateKey, certChain []*x509.Certificate) (*GenericSigner, error) { +func New(key crypto.PrivateKey, certChain []*x509.Certificate) (notation.Signer, error) { + return NewGenericSigner(key, certChain) +} + +// NewGenericSigner returns a builtinSigner given key and cert chain +func NewGenericSigner(key crypto.PrivateKey, certChain []*x509.Certificate) (*GenericSigner, error) { localSigner, err := signature.NewLocalSigner(certChain, key) if err != nil { return nil, err @@ -53,7 +58,12 @@ func New(key crypto.PrivateKey, certChain []*x509.Certificate) (*GenericSigner, } // NewFromFiles returns a builtinSigner given key and certChain paths. -func NewFromFiles(keyPath, certChainPath string) (*GenericSigner, error) { +func NewFromFiles(keyPath, certChainPath string) (notation.Signer, error) { + return NewGenericSignerFromFiles(keyPath, certChainPath) +} + +// NewGenericSignerFromFiles returns a builtinSigner given key and certChain paths. +func NewGenericSignerFromFiles(keyPath, certChainPath string) (*GenericSigner, error) { if keyPath == "" { return nil, errors.New("key path not specified") } @@ -80,7 +90,7 @@ func NewFromFiles(keyPath, certChainPath string) (*GenericSigner, error) { } // create signer - return New(cert.PrivateKey, certs) + return NewGenericSigner(cert.PrivateKey, certs) } // Sign signs the artifact described by its descriptor and returns the diff --git a/signer/signer_test.go b/signer/signer_test.go index 0124da2b..31e16ca5 100644 --- a/signer/signer_test.go +++ b/signer/signer_test.go @@ -219,7 +219,7 @@ func TestSignBlobWithCertChain(t *testing.T) { for _, envelopeType := range signature.RegisteredEnvelopeTypes() { for _, keyCert := range keyCertPairCollections { t.Run(fmt.Sprintf("envelopeType=%v_keySpec=%v", envelopeType, keyCert.keySpecName), func(t *testing.T) { - s, err := New(keyCert.key, keyCert.certs) + s, err := NewGenericSigner(keyCert.key, keyCert.certs) if err != nil { t.Fatalf("NewSigner() error = %v", err) } From 5614867765345feb0f29471a1ec434ae34f30008 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Mon, 11 Mar 2024 14:41:35 -0700 Subject: [PATCH 11/14] fixes Signed-off-by: Pritesh Bandi --- signer/plugin.go | 7 +------ signer/signer.go | 18 +++++++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index f546a913..b3f50128 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -121,13 +121,8 @@ func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDe return nil, nil, err } - digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] - if !ok { - return nil, nil, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm()) - } - // get descriptor to sign - desc, err := blobGenFunc(digestAlg) + desc, err := getDescriptor(ks, blobGenFunc) if err != nil { return nil, nil, err } diff --git a/signer/signer.go b/signer/signer.go index 56de756b..194068c3 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -158,7 +158,7 @@ func (s *GenericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts } // SignBlob signs the descriptor returned by blobGen and returns the marshalled envelope -func (s *GenericSigner) SignBlob(ctx context.Context, blobGen notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *GenericSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType) @@ -167,15 +167,19 @@ func (s *GenericSigner) SignBlob(ctx context.Context, blobGen notation.BlobDescr return nil, nil, err } - digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] - if !ok { - return nil, nil, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm().Hash()) - } - - desc, err := blobGen(digestAlg) + desc, err := getDescriptor(ks, blobGenFunc) if err != nil { return nil, nil, err } return s.Sign(ctx, desc, opts) } + +func getDescriptor(ks signature.KeySpec, blobGenFunc notation.BlobDescriptorGenerator) (ocispec.Descriptor, error) { + digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] + if !ok { + return ocispec.Descriptor{}, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm().Hash()) + } + + return blobGenFunc(digestAlg) +} From 5e8f245a75a3c55606cf11717bb22cf0f866d0c4 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Tue, 12 Mar 2024 14:39:46 -0700 Subject: [PATCH 12/14] fixes Signed-off-by: Pritesh Bandi --- signer/plugin.go | 4 ++-- signer/signer.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index b3f50128..ecf653da 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -105,7 +105,7 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n } // SignBlob signs the arbitrary data and returns the marshalled envelope. -func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *PluginSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) mergedConfig := s.mergeConfig(opts.PluginConfig) @@ -122,7 +122,7 @@ func (s *PluginSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDe } // get descriptor to sign - desc, err := getDescriptor(ks, blobGenFunc) + desc, err := getDescriptor(ks, descGenFunc) if err != nil { return nil, nil, err } diff --git a/signer/signer.go b/signer/signer.go index 194068c3..3e10c9dc 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -158,7 +158,7 @@ func (s *GenericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts } // SignBlob signs the descriptor returned by blobGen and returns the marshalled envelope -func (s *GenericSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { +func (s *GenericSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobDescriptorGenerator, opts notation.SignerSignOptions) ([]byte, *signature.SignerInfo, error) { logger := log.GetLogger(ctx) logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType) @@ -167,7 +167,7 @@ func (s *GenericSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobD return nil, nil, err } - desc, err := getDescriptor(ks, blobGenFunc) + desc, err := getDescriptor(ks, descGenFunc) if err != nil { return nil, nil, err } @@ -175,11 +175,11 @@ func (s *GenericSigner) SignBlob(ctx context.Context, blobGenFunc notation.BlobD return s.Sign(ctx, desc, opts) } -func getDescriptor(ks signature.KeySpec, blobGenFunc notation.BlobDescriptorGenerator) (ocispec.Descriptor, error) { +func getDescriptor(ks signature.KeySpec, descGenFunc notation.BlobDescriptorGenerator) (ocispec.Descriptor, error) { digestAlg, ok := algorithms[ks.SignatureAlgorithm().Hash()] if !ok { return ocispec.Descriptor{}, fmt.Errorf("unknown hashing algo %v", ks.SignatureAlgorithm().Hash()) } - return blobGenFunc(digestAlg) + return descGenFunc(digestAlg) } From e8f77a873401c731383415fcc806ceee28d5c85f Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Wed, 13 Mar 2024 16:55:27 -0700 Subject: [PATCH 13/14] Update notation.go Co-authored-by: Shiwei Zhang Signed-off-by: Pritesh Bandi --- notation.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notation.go b/notation.go index bb8cdaf6..8cb566a8 100644 --- a/notation.go +++ b/notation.go @@ -517,14 +517,14 @@ func generateAnnotations(signerInfo *signature.SignerInfo, annotations map[strin func getDescriptorFunc(ctx context.Context, reader io.Reader, contentMediaType string, userMetadata map[string]string) BlobDescriptorGenerator { return func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { - h := hashAlgo.Hash() - bytes, err := io.Copy(hashAlgo.Hash(), reader) + digester := hashAlgo.Digester() + bytes, err := io.Copy(digester.Hash(), reader) if err != nil { return ocispec.Descriptor{}, err } targetDesc := ocispec.Descriptor{ MediaType: contentMediaType, - Digest: digest.NewDigest(hashAlgo, h), + Digest: digester.Digest(), Size: bytes, } return addUserMetadataToDescriptor(ctx, targetDesc, userMetadata) From 2044e2b8294185151d0f9dda1be494d265283548 Mon Sep 17 00:00:00 2001 From: Pritesh Bandi Date: Wed, 13 Mar 2024 17:08:39 -0700 Subject: [PATCH 14/14] fixes Signed-off-by: Pritesh Bandi --- example_localSign_test.go | 4 ++-- example_remoteSign_test.go | 7 ++++--- signer/plugin.go | 12 +++++++----- signer/signer.go | 10 ++++++---- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/example_localSign_test.go b/example_localSign_test.go index 996784ce..9672c02f 100644 --- a/example_localSign_test.go +++ b/example_localSign_test.go @@ -47,8 +47,8 @@ func Example_localSign() { // Users should replace `exampleCertTuple.PrivateKey` with their own private // key and replace `exampleCerts` with the corresponding full certificate // chain, following the Notary certificate requirements: - // https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/signature-specification.md#certificate-requirements - exampleSigner, err := signer.New(exampleCertTuple.PrivateKey, exampleCerts) + // https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements + exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts) if err != nil { panic(err) // Handle error } diff --git a/example_remoteSign_test.go b/example_remoteSign_test.go index 9cf51058..03565e98 100644 --- a/example_remoteSign_test.go +++ b/example_remoteSign_test.go @@ -18,12 +18,13 @@ import ( "crypto/x509" "fmt" + "oras.land/oras-go/v2/registry/remote" + "github.com/notaryproject/notation-core-go/signature/cose" "github.com/notaryproject/notation-core-go/testhelper" "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/registry" "github.com/notaryproject/notation-go/signer" - "oras.land/oras-go/v2/registry/remote" ) // Both COSE ("application/cose") and JWS ("application/jose+json") @@ -45,8 +46,8 @@ func Example_remoteSign() { // Users should replace `exampleCertTuple.PrivateKey` with their own private // key and replace `exampleCerts` with the corresponding full certificate // chain, following the Notary certificate requirements: - // https://github.com/notaryproject/notaryproject/blob/v1.0.0-rc.1/specs/signature-specification.md#certificate-requirements - exampleSigner, err := signer.New(exampleCertTuple.PrivateKey, exampleCerts) + // https://github.com/notaryproject/notaryproject/blob/v1.0.0/specs/signature-specification.md#certificate-requirements + exampleSigner, err := signer.NewGenericSigner(exampleCertTuple.PrivateKey, exampleCerts) if err != nil { panic(err) // Handle error } diff --git a/signer/plugin.go b/signer/plugin.go index ecf653da..d0c96e6b 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -52,6 +52,8 @@ var algorithms = map[crypto.Hash]digest.Algorithm{ // NewFromPlugin creates a notation.Signer that signs artifacts and generates // signatures by delegating the one or more operations to the named plugin, // as defined in https://github.com/notaryproject/notaryproject/blob/main/specs/plugin-extensibility.md#signing-interfaces. +// Deprecated: NewFromPlugin function exists for historical compatibility and should not be used. +// To create PluginSigner, use NewPluginSigner() function. func NewFromPlugin(plugin plugin.SignPlugin, keyID string, pluginConfig map[string]string) (notation.Signer, error) { return NewPluginSigner(plugin, keyID, pluginConfig) } @@ -92,13 +94,13 @@ func (s *PluginSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts n } logger.Debugf("Using plugin %v with capabilities %v to sign oci artifact %v in signature media type %v", metadata.Name, metadata.Capabilities, desc.Digest, opts.SignatureMediaType) - if metadata.HasCapability(proto.CapabilitySignatureGenerator) { + if metadata.HasCapability(plugin.CapabilitySignatureGenerator) { ks, err := s.getKeySpec(ctx, mergedConfig) if err != nil { return nil, nil, err } return s.generateSignature(ctx, desc, opts, ks, metadata, mergedConfig) - } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { + } else if metadata.HasCapability(plugin.CapabilityEnvelopeGenerator) { return s.generateSignatureEnvelope(ctx, desc, opts) } return nil, nil, fmt.Errorf("plugin does not have signing capabilities") @@ -128,9 +130,9 @@ func (s *PluginSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobDe } logger.Debugf("Using plugin %v with capabilities %v to sign blob using descriptor %+v", metadata.Name, metadata.Capabilities, desc) - if metadata.HasCapability(proto.CapabilitySignatureGenerator) { + if metadata.HasCapability(plugin.CapabilitySignatureGenerator) { return s.generateSignature(ctx, desc, opts, ks, metadata, mergedConfig) - } else if metadata.HasCapability(proto.CapabilityEnvelopeGenerator) { + } else if metadata.HasCapability(plugin.CapabilityEnvelopeGenerator) { return s.generateSignatureEnvelope(ctx, desc, opts) } return nil, nil, fmt.Errorf("plugin does not have signing capabilities") @@ -155,7 +157,7 @@ func (s *PluginSigner) generateSignature(ctx context.Context, desc ocispec.Descr logger := log.GetLogger(ctx) logger.Debug("Generating signature by plugin") genericSigner := GenericSigner{ - Signer: &pluginPrimitiveSigner{ + signer: &pluginPrimitiveSigner{ ctx: ctx, plugin: s.plugin, keyID: s.keyID, diff --git a/signer/signer.go b/signer/signer.go index 3e10c9dc..05d0ea8c 100644 --- a/signer/signer.go +++ b/signer/signer.go @@ -38,10 +38,12 @@ const signingAgent = "Notation/1.0.0" // GenericSigner implements notation.Signer and embeds signature.Signer type GenericSigner struct { - signature.Signer + signer signature.Signer } // New returns a builtinSigner given key and cert chain +// Deprecated: New function exists for historical compatibility and should not be used. +// To create GenericSigner, use NewGenericSigner() function. func New(key crypto.PrivateKey, certChain []*x509.Certificate) (notation.Signer, error) { return NewGenericSigner(key, certChain) } @@ -53,7 +55,7 @@ func NewGenericSigner(key crypto.PrivateKey, certChain []*x509.Certificate) (*Ge return nil, err } return &GenericSigner{ - Signer: localSigner, + signer: localSigner, }, nil } @@ -116,7 +118,7 @@ func (s *GenericSigner) Sign(ctx context.Context, desc ocispec.Descriptor, opts ContentType: envelope.MediaTypePayloadV1, Content: payloadBytes, }, - Signer: s.Signer, + Signer: s.signer, SigningTime: time.Now(), SigningScheme: signature.SigningSchemeX509, SigningAgent: signingAgentId, @@ -162,7 +164,7 @@ func (s *GenericSigner) SignBlob(ctx context.Context, descGenFunc notation.BlobD logger := log.GetLogger(ctx) logger.Debugf("Generic blob signing for signature media type %v", opts.SignatureMediaType) - ks, err := s.KeySpec() + ks, err := s.signer.KeySpec() if err != nil { return nil, nil, err }