From ec13c70b9e67447182ac62342dae8e8a8ffd79fb Mon Sep 17 00:00:00 2001 From: Dallas Delaney Date: Thu, 15 Jan 2026 17:34:48 -0800 Subject: [PATCH 1/4] signer: export PluginPrimitiveSigner for dm-verity signing Signed-off-by: Dallas Delaney --- signer/plugin.go | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index 6d8615d7..6c0d353c 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -162,7 +162,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, @@ -325,8 +325,8 @@ func parseCertChain(certChain [][]byte) ([]*x509.Certificate, error) { return certs, nil } -// pluginPrimitiveSigner implements signature.Signer -type pluginPrimitiveSigner struct { +// PluginPrimitiveSigner implements signature.Signer +type PluginPrimitiveSigner struct { ctx context.Context plugin plugin.SignPlugin keyID string @@ -335,7 +335,7 @@ type pluginPrimitiveSigner struct { } // Sign signs the digest by calling the underlying plugin. -func (s *pluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificate, error) { +func (s *PluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificate, error) { // Execute plugin sign command. keySpec, err := proto.EncodeKeySpec(s.keySpec) if err != nil { @@ -372,6 +372,35 @@ func (s *pluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificat // KeySpec returns the keySpec of a keyID by calling describeKey and do some // keySpec validation. -func (s *pluginPrimitiveSigner) KeySpec() (signature.KeySpec, error) { +func (s *PluginPrimitiveSigner) KeySpec() (signature.KeySpec, error) { return s.keySpec, nil } + +// NewPluginPrimitiveSigner creates a new PluginPrimitiveSigner that delegates +// signing to a plugin. This is used for dm-verity PKCS#7 signing where raw +// signature bytes are needed instead of JWS/COSE envelopes. +func NewPluginPrimitiveSigner(ctx context.Context, p plugin.SignPlugin, keyID string, keySpec signature.KeySpec, pluginConfig map[string]string) *PluginPrimitiveSigner { + return &PluginPrimitiveSigner{ + ctx: ctx, + plugin: p, + keyID: keyID, + keySpec: keySpec, + pluginConfig: pluginConfig, + } +} + +// GetKeySpecFromPlugin retrieves the key specification from a plugin by calling DescribeKey. +func GetKeySpecFromPlugin(ctx context.Context, p plugin.SignPlugin, keyID string, pluginConfig map[string]string) (signature.KeySpec, error) { + req := &plugin.DescribeKeyRequest{ + ContractVersion: plugin.ContractVersion, + KeyID: keyID, + PluginConfig: pluginConfig, + } + + resp, err := p.DescribeKey(ctx, req) + if err != nil { + return signature.KeySpec{}, err + } + + return proto.DecodeKeySpec(resp.KeySpec) +} From ac45f3ea83b81b36d2216460717d78b0097baa07 Mon Sep 17 00:00:00 2001 From: Dallas Delaney Date: Mon, 9 Mar 2026 16:35:05 -0700 Subject: [PATCH 2/4] test: add unit tests for exported PluginPrimitiveSigner and GetKeySpecFromPlugin Signed-off-by: Dallas Delaney --- signer/plugin_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/signer/plugin_test.go b/signer/plugin_test.go index a98caa07..5d839d56 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -166,7 +166,7 @@ func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *proto.GenerateEn return nil, err } - primitivePluginSigner := &pluginPrimitiveSigner{ + primitivePluginSigner := &PluginPrimitiveSigner{ ctx: ctx, plugin: internalPluginSigner.plugin, keyID: internalPluginSigner.keyID, @@ -492,3 +492,53 @@ func basicSignTest(t *testing.T, ps *PluginSigner, envelopeType string, data []b } basicVerification(t, data, envelopeType, mockPlugin.certs[len(mockPlugin.certs)-1], &validMetadata) } + +func TestNewPluginPrimitiveSigner(t *testing.T) { + ctx := context.Background() + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + + s := NewPluginPrimitiveSigner(ctx, mp, "testKeyID", defaultKeySpec, nil) + + // verify KeySpec + ks, err := s.KeySpec() + if err != nil { + t.Fatalf("KeySpec() error: %v", err) + } + if ks != defaultKeySpec { + t.Fatalf("KeySpec() = %v, want %v", ks, defaultKeySpec) + } + + // verify Sign + sig, certs, err := s.Sign([]byte("payload")) + if err != nil { + t.Fatalf("Sign() error: %v", err) + } + if len(sig) == 0 { + t.Fatal("Sign() returned empty signature") + } + if len(certs) == 0 { + t.Fatal("Sign() returned no certificates") + } +} + +func TestGetKeySpecFromPlugin(t *testing.T) { + ctx := context.Background() + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + + got, err := GetKeySpecFromPlugin(ctx, mp, "testKeyID", nil) + if err != nil { + t.Fatalf("GetKeySpecFromPlugin() error: %v", err) + } + if got != defaultKeySpec { + t.Fatalf("GetKeySpecFromPlugin() = %v, want %v", got, defaultKeySpec) + } +} + +func TestGetKeySpecFromPlugin_Error(t *testing.T) { + ctx := context.Background() + mp := &mockPlugin{} + _, err := GetKeySpecFromPlugin(ctx, mp, "testKeyID", nil) + if err == nil { + t.Fatal("expected error for empty keySpec, got nil") + } +} From 1f82a42b2e6eccf566d5a64416077ccda12f7ab8 Mon Sep 17 00:00:00 2001 From: Dallas Delaney Date: Wed, 6 May 2026 14:58:58 -0700 Subject: [PATCH 3/4] feat: expand test coverage and update API names and return types Signed-off-by: Dallas Delaney --- signer/plugin.go | 64 ++++++++++++---- signer/plugin_test.go | 166 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 196 insertions(+), 34 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index 6c0d353c..601244bb 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -325,7 +325,14 @@ func parseCertChain(certChain [][]byte) ([]*x509.Certificate, error) { return certs, nil } -// PluginPrimitiveSigner implements signature.Signer +// PluginPrimitiveSigner implements signature.Signer by delegating raw +// signature generation to a plugin.SignPlugin. +// +// Unlike PluginSigner, which produces a complete JWS/COSE signature +// envelope for OCI artifact signing, PluginPrimitiveSigner returns the raw +// signature bytes plus the signing certificate chain. This is required +// by signature envelopes that build their own structure around a +// pre-computed signature, such as PKCS#7. type PluginPrimitiveSigner struct { ctx context.Context plugin plugin.SignPlugin @@ -334,8 +341,13 @@ type PluginPrimitiveSigner struct { keySpec signature.KeySpec } -// Sign signs the digest by calling the underlying plugin. +// Sign signs the payload by calling the underlying plugin's GenerateSignature command +// and returns the raw signature bytes together with the signing certificate +// chain returned by the plugin. func (s *PluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificate, error) { + if s == nil || s.plugin == nil { + return nil, nil, errors.New("PluginPrimitiveSigner not initialized: use NewPluginPrimitiveSigner") + } // Execute plugin sign command. keySpec, err := proto.EncodeKeySpec(s.keySpec) if err != nil { @@ -370,37 +382,57 @@ func (s *PluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificat return resp.Signature, certs, nil } -// KeySpec returns the keySpec of a keyID by calling describeKey and do some -// keySpec validation. +// KeySpec returns the KeySpec that the signer was constructed with. +// The keySpec is supplied at construction time (typically via KeySpecFromPlugin). func (s *PluginPrimitiveSigner) KeySpec() (signature.KeySpec, error) { + if s == nil || s.plugin == nil { + return signature.KeySpec{}, errors.New("PluginPrimitiveSigner not initialized: use NewPluginPrimitiveSigner") + } return s.keySpec, nil } // NewPluginPrimitiveSigner creates a new PluginPrimitiveSigner that delegates -// signing to a plugin. This is used for dm-verity PKCS#7 signing where raw -// signature bytes are needed instead of JWS/COSE envelopes. -func NewPluginPrimitiveSigner(ctx context.Context, p plugin.SignPlugin, keyID string, keySpec signature.KeySpec, pluginConfig map[string]string) *PluginPrimitiveSigner { +// signing to a plugin. It is intended for callers that need raw signature +// bytes (PKCS#7) rather than a JWS/COSE envelope. +func NewPluginPrimitiveSigner(ctx context.Context, p plugin.SignPlugin, keyID string, keySpec signature.KeySpec, pluginConfig map[string]string) (signature.Signer, error) { + if p == nil { + return nil, errors.New("nil plugin") + } + if keyID == "" { + return nil, errors.New("keyID not specified") + } + if _, err := proto.HashAlgorithmFromKeySpec(keySpec); err != nil { + return nil, fmt.Errorf("invalid keySpec: %w", err) + } return &PluginPrimitiveSigner{ ctx: ctx, plugin: p, keyID: keyID, keySpec: keySpec, pluginConfig: pluginConfig, - } + }, nil } -// GetKeySpecFromPlugin retrieves the key specification from a plugin by calling DescribeKey. -func GetKeySpecFromPlugin(ctx context.Context, p plugin.SignPlugin, keyID string, pluginConfig map[string]string) (signature.KeySpec, error) { - req := &plugin.DescribeKeyRequest{ +// KeySpecFromPlugin retrieves and validates the key specification from a +// plugin by calling DescribeKey. It enforces that the plugin's response +// references the same keyID that was requested. +func KeySpecFromPlugin(ctx context.Context, p plugin.SignPlugin, keyID string, pluginConfig map[string]string) (signature.KeySpec, error) { + if p == nil { + return signature.KeySpec{}, errors.New("nil plugin") + } + if keyID == "" { + return signature.KeySpec{}, errors.New("keyID not specified") + } + resp, err := p.DescribeKey(ctx, &plugin.DescribeKeyRequest{ ContractVersion: plugin.ContractVersion, KeyID: keyID, PluginConfig: pluginConfig, - } - - resp, err := p.DescribeKey(ctx, req) + }) if err != nil { - return signature.KeySpec{}, err + return signature.KeySpec{}, fmt.Errorf("failed to describe key %q: %w", keyID, err) + } + if resp.KeyID != "" && resp.KeyID != keyID { + return signature.KeySpec{}, fmt.Errorf("keyID in describeKey response %q does not match request %q", resp.KeyID, keyID) } - return proto.DecodeKeySpec(resp.KeySpec) } diff --git a/signer/plugin_test.go b/signer/plugin_test.go index 5d839d56..1b226cba 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -60,15 +60,17 @@ func init() { } type mockPlugin struct { - failEnvelope bool - wantEnvelope bool - invalidSig bool - invalidCertChain bool - invalidDescriptor bool - annotations map[string]string - key crypto.PrivateKey - certs []*x509.Certificate - keySpec signature.KeySpec + failEnvelope bool + wantEnvelope bool + invalidSig bool + invalidCertChain bool + invalidDescriptor bool + describeKeyErr error + describeKeyIDOverride string + annotations map[string]string + key crypto.PrivateKey + certs []*x509.Certificate + keySpec signature.KeySpec } func getDescriptorFunc(throwError bool) func(hashAlgo digest.Algorithm) (ocispec.Descriptor, error) { @@ -108,8 +110,15 @@ func (p *mockPlugin) GetMetadata(ctx context.Context, req *proto.GetMetadataRequ // DescribeKey returns the KeySpec of a key. func (p *mockPlugin) DescribeKey(ctx context.Context, req *proto.DescribeKeyRequest) (*proto.DescribeKeyResponse, error) { + if p.describeKeyErr != nil { + return nil, p.describeKeyErr + } ks, _ := proto.EncodeKeySpec(p.keySpec) + // Default behavior matches the historical mock: KeyID is empty unless + // describeKeyIDOverride is set. Tests that need a non-empty (and + // possibly mismatched) keyID set the override explicitly. return &proto.DescribeKeyResponse{ + KeyID: p.describeKeyIDOverride, KeySpec: ks, }, nil } @@ -497,7 +506,10 @@ func TestNewPluginPrimitiveSigner(t *testing.T) { ctx := context.Background() mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) - s := NewPluginPrimitiveSigner(ctx, mp, "testKeyID", defaultKeySpec, nil) + s, err := NewPluginPrimitiveSigner(ctx, mp, "testKeyID", defaultKeySpec, nil) + if err != nil { + t.Fatalf("NewPluginPrimitiveSigner() error: %v", err) + } // verify KeySpec ks, err := s.KeySpec() @@ -521,24 +533,142 @@ func TestNewPluginPrimitiveSigner(t *testing.T) { } } -func TestGetKeySpecFromPlugin(t *testing.T) { +func TestNewPluginPrimitiveSigner_NilPlugin(t *testing.T) { + _, err := NewPluginPrimitiveSigner(context.Background(), nil, "testKeyID", defaultKeySpec, nil) + if err == nil { + t.Fatal("expected error for nil plugin, got nil") + } + if !strings.Contains(err.Error(), "nil plugin") { + t.Errorf("unexpected error: %v", err) + } +} + +func TestNewPluginPrimitiveSigner_EmptyKeyID(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + _, err := NewPluginPrimitiveSigner(context.Background(), mp, "", defaultKeySpec, nil) + if err == nil { + t.Fatal("expected error for empty keyID, got nil") + } + if !strings.Contains(err.Error(), "keyID") { + t.Errorf("unexpected error: %v", err) + } +} + +func TestNewPluginPrimitiveSigner_InvalidKeySpec(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + _, err := NewPluginPrimitiveSigner(context.Background(), mp, "testKeyID", signature.KeySpec{}, nil) + if err == nil { + t.Fatal("expected error for invalid keySpec, got nil") + } + if !strings.Contains(err.Error(), "invalid keySpec") { + t.Errorf("unexpected error: %v", err) + } +} + +func TestPluginPrimitiveSigner_ZeroValue(t *testing.T) { + s := &PluginPrimitiveSigner{} + if _, _, err := s.Sign([]byte("x")); err == nil { + t.Error("Sign() on zero value: expected error, got nil") + } + if _, err := s.KeySpec(); err == nil { + t.Error("KeySpec() on zero value: expected error, got nil") + } +} + +func TestPluginPrimitiveSigner_SignError(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + mp.invalidCertChain = true // plugin returns unparseable cert chain + s, err := NewPluginPrimitiveSigner(context.Background(), mp, "testKeyID", defaultKeySpec, nil) + if err != nil { + t.Fatalf("NewPluginPrimitiveSigner() error: %v", err) + } + if _, _, err := s.Sign([]byte("payload")); err == nil { + t.Fatal("expected Sign() to fail when plugin returns invalid cert chain, got nil") + } +} + +func TestPluginPrimitiveSigner_ECDSA(t *testing.T) { + var ecPair *keyCertPair + var ecKeySpec signature.KeySpec + for _, p := range keyCertPairCollections { + ks, err := signature.ExtractKeySpec(p.certs[0]) + if err == nil && ks.Type == signature.KeyTypeEC { + ecPair = p + ecKeySpec = ks + break + } + } + if ecPair == nil { + t.Skip("no EC keyCertPair available in test fixtures") + } + mp := newMockPlugin(ecPair.key, ecPair.certs, ecKeySpec) + s, err := NewPluginPrimitiveSigner(context.Background(), mp, "testKeyID", ecKeySpec, nil) + if err != nil { + t.Fatalf("NewPluginPrimitiveSigner() error: %v", err) + } + sig, certs, err := s.Sign([]byte("payload")) + if err != nil { + t.Fatalf("Sign() error: %v", err) + } + if len(sig) == 0 || len(certs) == 0 { + t.Fatal("Sign() returned empty result") + } +} + +func TestKeySpecFromPlugin(t *testing.T) { ctx := context.Background() mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) - got, err := GetKeySpecFromPlugin(ctx, mp, "testKeyID", nil) + got, err := KeySpecFromPlugin(ctx, mp, "testKeyID", nil) if err != nil { - t.Fatalf("GetKeySpecFromPlugin() error: %v", err) + t.Fatalf("KeySpecFromPlugin() error: %v", err) } if got != defaultKeySpec { - t.Fatalf("GetKeySpecFromPlugin() = %v, want %v", got, defaultKeySpec) + t.Fatalf("KeySpecFromPlugin() = %v, want %v", got, defaultKeySpec) } } -func TestGetKeySpecFromPlugin_Error(t *testing.T) { - ctx := context.Background() +func TestKeySpecFromPlugin_NilPlugin(t *testing.T) { + if _, err := KeySpecFromPlugin(context.Background(), nil, "testKeyID", nil); err == nil { + t.Fatal("expected error for nil plugin, got nil") + } +} + +func TestKeySpecFromPlugin_EmptyKeyID(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + if _, err := KeySpecFromPlugin(context.Background(), mp, "", nil); err == nil { + t.Fatal("expected error for empty keyID, got nil") + } +} + +func TestKeySpecFromPlugin_KeyIDMismatch(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + mp.describeKeyIDOverride = "differentKeyID" + _, err := KeySpecFromPlugin(context.Background(), mp, "testKeyID", nil) + if err == nil { + t.Fatal("expected keyID mismatch error, got nil") + } + if !strings.Contains(err.Error(), "does not match") { + t.Errorf("unexpected error: %v", err) + } +} + +func TestKeySpecFromPlugin_DescribeKeyError(t *testing.T) { + mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + mp.describeKeyErr = errors.New("simulated describeKey failure") + _, err := KeySpecFromPlugin(context.Background(), mp, "testKeyID", nil) + if err == nil { + t.Fatal("expected describeKey error to propagate, got nil") + } + if !strings.Contains(err.Error(), "failed to describe key") { + t.Errorf("expected wrapped error, got: %v", err) + } +} + +func TestKeySpecFromPlugin_DecodeError(t *testing.T) { mp := &mockPlugin{} - _, err := GetKeySpecFromPlugin(ctx, mp, "testKeyID", nil) + _, err := KeySpecFromPlugin(context.Background(), mp, "testKeyID", nil) if err == nil { - t.Fatal("expected error for empty keySpec, got nil") + t.Fatal("expected decode error for empty keySpec, got nil") } } From bbc31b233e8f298f0d27c99ecc637498b3536ec3 Mon Sep 17 00:00:00 2001 From: Dallas Delaney Date: Thu, 7 May 2026 16:06:54 -0700 Subject: [PATCH 4/4] feat: Unexport pluginPrimitiveSigner now that constructor returns signature.Signer. Clean up test logs Signed-off-by: Dallas Delaney --- signer/plugin.go | 41 +++++++++++++---------------------------- signer/plugin_test.go | 24 ++---------------------- 2 files changed, 15 insertions(+), 50 deletions(-) diff --git a/signer/plugin.go b/signer/plugin.go index 601244bb..7a01051f 100644 --- a/signer/plugin.go +++ b/signer/plugin.go @@ -162,7 +162,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, @@ -325,15 +325,8 @@ func parseCertChain(certChain [][]byte) ([]*x509.Certificate, error) { return certs, nil } -// PluginPrimitiveSigner implements signature.Signer by delegating raw -// signature generation to a plugin.SignPlugin. -// -// Unlike PluginSigner, which produces a complete JWS/COSE signature -// envelope for OCI artifact signing, PluginPrimitiveSigner returns the raw -// signature bytes plus the signing certificate chain. This is required -// by signature envelopes that build their own structure around a -// pre-computed signature, such as PKCS#7. -type PluginPrimitiveSigner struct { +// pluginPrimitiveSigner implements signature.Signer +type pluginPrimitiveSigner struct { ctx context.Context plugin plugin.SignPlugin keyID string @@ -341,13 +334,8 @@ type PluginPrimitiveSigner struct { keySpec signature.KeySpec } -// Sign signs the payload by calling the underlying plugin's GenerateSignature command -// and returns the raw signature bytes together with the signing certificate -// chain returned by the plugin. -func (s *PluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificate, error) { - if s == nil || s.plugin == nil { - return nil, nil, errors.New("PluginPrimitiveSigner not initialized: use NewPluginPrimitiveSigner") - } +// Sign signs the digest by calling the underlying plugin. +func (s *pluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificate, error) { // Execute plugin sign command. keySpec, err := proto.EncodeKeySpec(s.keySpec) if err != nil { @@ -382,18 +370,15 @@ func (s *PluginPrimitiveSigner) Sign(payload []byte) ([]byte, []*x509.Certificat return resp.Signature, certs, nil } -// KeySpec returns the KeySpec that the signer was constructed with. -// The keySpec is supplied at construction time (typically via KeySpecFromPlugin). -func (s *PluginPrimitiveSigner) KeySpec() (signature.KeySpec, error) { - if s == nil || s.plugin == nil { - return signature.KeySpec{}, errors.New("PluginPrimitiveSigner not initialized: use NewPluginPrimitiveSigner") - } +// KeySpec returns the keySpec of a keyID by calling describeKey and do some +// keySpec validation. +func (s *pluginPrimitiveSigner) KeySpec() (signature.KeySpec, error) { return s.keySpec, nil } -// NewPluginPrimitiveSigner creates a new PluginPrimitiveSigner that delegates -// signing to a plugin. It is intended for callers that need raw signature -// bytes (PKCS#7) rather than a JWS/COSE envelope. +// NewPluginPrimitiveSigner returns a signature.Signer that delegates raw +// signature generation to a plugin. It is intended for callers that need raw +// signature bytes (PKCS#7) rather than a JWS/COSE envelope. func NewPluginPrimitiveSigner(ctx context.Context, p plugin.SignPlugin, keyID string, keySpec signature.KeySpec, pluginConfig map[string]string) (signature.Signer, error) { if p == nil { return nil, errors.New("nil plugin") @@ -404,7 +389,7 @@ func NewPluginPrimitiveSigner(ctx context.Context, p plugin.SignPlugin, keyID st if _, err := proto.HashAlgorithmFromKeySpec(keySpec); err != nil { return nil, fmt.Errorf("invalid keySpec: %w", err) } - return &PluginPrimitiveSigner{ + return &pluginPrimitiveSigner{ ctx: ctx, plugin: p, keyID: keyID, @@ -431,7 +416,7 @@ func KeySpecFromPlugin(ctx context.Context, p plugin.SignPlugin, keyID string, p if err != nil { return signature.KeySpec{}, fmt.Errorf("failed to describe key %q: %w", keyID, err) } - if resp.KeyID != "" && resp.KeyID != keyID { + if resp.KeyID != keyID { return signature.KeySpec{}, fmt.Errorf("keyID in describeKey response %q does not match request %q", resp.KeyID, keyID) } return proto.DecodeKeySpec(resp.KeySpec) diff --git a/signer/plugin_test.go b/signer/plugin_test.go index 1b226cba..34ea1b8b 100644 --- a/signer/plugin_test.go +++ b/signer/plugin_test.go @@ -114,9 +114,6 @@ func (p *mockPlugin) DescribeKey(ctx context.Context, req *proto.DescribeKeyRequ return nil, p.describeKeyErr } ks, _ := proto.EncodeKeySpec(p.keySpec) - // Default behavior matches the historical mock: KeyID is empty unless - // describeKeyIDOverride is set. Tests that need a non-empty (and - // possibly mismatched) keyID set the override explicitly. return &proto.DescribeKeyResponse{ KeyID: p.describeKeyIDOverride, KeySpec: ks, @@ -175,7 +172,7 @@ func (p *mockPlugin) GenerateEnvelope(ctx context.Context, req *proto.GenerateEn return nil, err } - primitivePluginSigner := &PluginPrimitiveSigner{ + primitivePluginSigner := &pluginPrimitiveSigner{ ctx: ctx, plugin: internalPluginSigner.plugin, keyID: internalPluginSigner.keyID, @@ -565,16 +562,6 @@ func TestNewPluginPrimitiveSigner_InvalidKeySpec(t *testing.T) { } } -func TestPluginPrimitiveSigner_ZeroValue(t *testing.T) { - s := &PluginPrimitiveSigner{} - if _, _, err := s.Sign([]byte("x")); err == nil { - t.Error("Sign() on zero value: expected error, got nil") - } - if _, err := s.KeySpec(); err == nil { - t.Error("KeySpec() on zero value: expected error, got nil") - } -} - func TestPluginPrimitiveSigner_SignError(t *testing.T) { mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) mp.invalidCertChain = true // plugin returns unparseable cert chain @@ -618,6 +605,7 @@ func TestPluginPrimitiveSigner_ECDSA(t *testing.T) { func TestKeySpecFromPlugin(t *testing.T) { ctx := context.Background() mp := newMockPlugin(defaultKeyCert.key, defaultKeyCert.certs, defaultKeySpec) + mp.describeKeyIDOverride = "testKeyID" got, err := KeySpecFromPlugin(ctx, mp, "testKeyID", nil) if err != nil { @@ -664,11 +652,3 @@ func TestKeySpecFromPlugin_DescribeKeyError(t *testing.T) { t.Errorf("expected wrapped error, got: %v", err) } } - -func TestKeySpecFromPlugin_DecodeError(t *testing.T) { - mp := &mockPlugin{} - _, err := KeySpecFromPlugin(context.Background(), mp, "testKeyID", nil) - if err == nil { - t.Fatal("expected decode error for empty keySpec, got nil") - } -}