From ebf9abc43636ed1966fb61bdaa76552666cc5e58 Mon Sep 17 00:00:00 2001 From: Prashant Yadav Date: Tue, 24 Mar 2026 11:59:39 -0700 Subject: [PATCH 1/5] EnsureRightLabelOnSecret: dual label support for workflow_owner and org_id Update EnsureRightLabelOnSecret to accept explicit workflowOwner and orgID parameters, supporting both ETH address (left-padded) and SHA256(org_id) label encodings. Centralize all label utilities in vaultutils/labels.go. Made-with: Cursor --- core/capabilities/vault/validator.go | 35 +++- core/capabilities/vault/validator_test.go | 205 +++++++++++++++++++ core/capabilities/vault/vaultutils/labels.go | 52 +++++ core/services/ocr2/plugins/vault/plugin.go | 4 +- system-tests/lib/cre/features/vault/vault.go | 13 +- 5 files changed, 285 insertions(+), 24 deletions(-) create mode 100644 core/capabilities/vault/validator_test.go create mode 100644 core/capabilities/vault/vaultutils/labels.go diff --git a/core/capabilities/vault/validator.go b/core/capabilities/vault/validator.go index 7309ecd07fb..2a15d60c078 100644 --- a/core/capabilities/vault/validator.go +++ b/core/capabilities/vault/validator.go @@ -7,12 +7,12 @@ import ( "fmt" "strconv" - "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" vaultcommon "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault" "github.com/smartcontractkit/chainlink-common/pkg/settings/limits" "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaulttypes" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaultutils" ) type RequestValidator struct { @@ -60,7 +60,7 @@ func (r *RequestValidator) validateWriteRequest(publicKey *tdh2easy.PublicKey, i if req.EncryptedValue == "" { return errors.New("secret must have encrypted value set at index " + strconv.Itoa(idx) + ":" + req.Id.String()) } - err := EnsureRightLabelOnSecret(publicKey, req.EncryptedValue, req.Id.Owner) + err := EnsureRightLabelOnSecret(publicKey, req.EncryptedValue, req.Id.Owner, "") if err != nil { return errors.New("Encrypted Secret at index [" + strconv.Itoa(idx) + "] doesn't have owner as the label. Error: " + err.Error()) } @@ -135,15 +135,19 @@ func NewRequestValidator(maxRequestBatchSizeLimiter limits.BoundLimiter[int]) *R } } -func EnsureRightLabelOnSecret(publicKey *tdh2easy.PublicKey, secret, owner string) error { +// EnsureRightLabelOnSecret verifies that the TDH2 ciphertext label matches either the +// workflowOwner (Ethereum address, left-padded) or the orgID (SHA256 hash). Either +// parameter can be empty to skip that check. The function succeeds if the label matches +// at least one non-empty owner. +func EnsureRightLabelOnSecret(publicKey *tdh2easy.PublicKey, secret string, workflowOwner string, orgID string) error { cipherText := &tdh2easy.Ciphertext{} cipherBytes, err := hex.DecodeString(secret) if err != nil { return errors.New("failed to decode encrypted value:" + err.Error()) } if publicKey == nil { - // Public key can be nil if gateway cache isn't populated yet(immediately after gateway reboots) - // Ok to not validate in such cases, since this validation also runs on Vault Nodes + // Public key can be nil if gateway cache isn't populated yet (immediately after gateway reboots). + // Ok to not validate in such cases, since this validation also runs on Vault Nodes. return nil } err = cipherText.UnmarshalVerify(cipherBytes, publicKey) @@ -151,11 +155,20 @@ func EnsureRightLabelOnSecret(publicKey *tdh2easy.PublicKey, secret, owner strin return errors.New("failed to verify encrypted value:" + err.Error()) } secretLabel := cipherText.Label() - ownerAddr := common.HexToAddress(owner) - var ownerLabel [32]byte - copy(ownerLabel[12:], ownerAddr.Bytes()) // left-pad with 12 zero - if secretLabel != ownerLabel { - return errors.New("secret label [" + hex.EncodeToString(secretLabel[:]) + "] does not match owner label [" + hex.EncodeToString(ownerLabel[:]) + "]") + + if workflowOwner != "" { + expected := vaultutils.OwnerToLabel(workflowOwner) + if secretLabel == expected { + return nil + } } - return nil + + if orgID != "" { + expected := vaultutils.OwnerToLabel(orgID) + if secretLabel == expected { + return nil + } + } + + return errors.New("secret label [" + hex.EncodeToString(secretLabel[:]) + "] does not match any of the provided owner labels") } diff --git a/core/capabilities/vault/validator_test.go b/core/capabilities/vault/validator_test.go new file mode 100644 index 00000000000..321bd059aed --- /dev/null +++ b/core/capabilities/vault/validator_test.go @@ -0,0 +1,205 @@ +package vault + +import ( + "crypto/sha256" + "encoding/hex" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaultutils" +) + +func generateTestKeys(t *testing.T) (*tdh2easy.PublicKey, []*tdh2easy.PrivateShare) { + t.Helper() + _, pk, shares, err := tdh2easy.GenerateKeys(1, 3) + require.NoError(t, err) + return pk, shares +} + +func encryptWithEthAddressLabel(t *testing.T, pk *tdh2easy.PublicKey, owner string) string { + t.Helper() + encrypted, err := vaultutils.EncryptSecretWithWorkflowOwner("test-secret", pk, common.HexToAddress(owner)) + require.NoError(t, err) + return encrypted +} + +func encryptWithOrgIDLabel(t *testing.T, pk *tdh2easy.PublicKey, orgID string) string { + t.Helper() + encrypted, err := vaultutils.EncryptSecretWithOrgID("test-secret", pk, orgID) + require.NoError(t, err) + return encrypted +} + +func TestOwnerToLabel(t *testing.T) { + t.Run("ethereum address with 0x prefix", func(t *testing.T) { + addr := "0x0001020304050607080900010203040506070809" + label := vaultutils.OwnerToLabel(addr) + + var expected [32]byte + copy(expected[12:], common.HexToAddress(addr).Bytes()) + assert.Equal(t, expected, label) + }) + + t.Run("ethereum address without 0x prefix", func(t *testing.T) { + addr := "0001020304050607080900010203040506070809" + label := vaultutils.OwnerToLabel(addr) + + var expected [32]byte + copy(expected[12:], common.HexToAddress(addr).Bytes()) + assert.Equal(t, expected, label) + }) + + t.Run("org_id produces SHA256 label", func(t *testing.T) { + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + label := vaultutils.OwnerToLabel(orgID) + + expected := sha256.Sum256([]byte(orgID)) + assert.Equal(t, expected, label) + }) + + t.Run("short string is not an ETH address", func(t *testing.T) { + owner := "my-org-id" + label := vaultutils.OwnerToLabel(owner) + + expected := sha256.Sum256([]byte(owner)) + assert.Equal(t, expected, label) + }) + + t.Run("checksummed ethereum address", func(t *testing.T) { + addr := "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" + label := vaultutils.OwnerToLabel(addr) + + var expected [32]byte + copy(expected[12:], common.HexToAddress(addr).Bytes()) + assert.Equal(t, expected, label) + }) +} + +func TestEnsureRightLabelOnSecret_WorkflowOwnerOnly(t *testing.T) { + pk, _ := generateTestKeys(t) + owner := "0x0001020304050607080900010203040506070809" + secret := encryptWithEthAddressLabel(t, pk, owner) + + err := EnsureRightLabelOnSecret(pk, secret, owner, "") + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_OrgIDOnly(t *testing.T) { + pk, _ := generateTestKeys(t) + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + secret := encryptWithOrgIDLabel(t, pk, orgID) + + err := EnsureRightLabelOnSecret(pk, secret, "", orgID) + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_DualMatchesWorkflowOwner(t *testing.T) { + pk, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + secret := encryptWithEthAddressLabel(t, pk, ethAddr) + + err := EnsureRightLabelOnSecret(pk, secret, ethAddr, orgID) + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_DualMatchesOrgID(t *testing.T) { + pk, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + secret := encryptWithOrgIDLabel(t, pk, orgID) + + err := EnsureRightLabelOnSecret(pk, secret, ethAddr, orgID) + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_NeitherMatches(t *testing.T) { + pk, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + wrongAddr := "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + wrongOrgID := "org_wrong" + secret := encryptWithEthAddressLabel(t, pk, ethAddr) + + err := EnsureRightLabelOnSecret(pk, secret, wrongAddr, wrongOrgID) + assert.Error(t, err) + assert.Contains(t, err.Error(), "does not match any of the provided owner labels") +} + +func TestEnsureRightLabelOnSecret_BothEmpty(t *testing.T) { + pk, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + secret := encryptWithEthAddressLabel(t, pk, ethAddr) + + err := EnsureRightLabelOnSecret(pk, secret, "", "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "does not match any of the provided owner labels") +} + +func TestEnsureRightLabelOnSecret_NilPublicKey(t *testing.T) { + pk, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + secret := encryptWithEthAddressLabel(t, pk, ethAddr) + + err := EnsureRightLabelOnSecret(nil, secret, ethAddr, "") + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_InvalidHexSecret(t *testing.T) { + pk, _ := generateTestKeys(t) + + err := EnsureRightLabelOnSecret(pk, "not-valid-hex!", "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to decode encrypted value") +} + +func TestEnsureRightLabelOnSecret_InvalidCiphertext(t *testing.T) { + pk, _ := generateTestKeys(t) + + err := EnsureRightLabelOnSecret(pk, hex.EncodeToString([]byte("garbage")), "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to verify encrypted value") +} + +func TestEnsureRightLabelOnSecret_WrongPublicKey(t *testing.T) { + pk, _ := generateTestKeys(t) + wrongPK, _ := generateTestKeys(t) + ethAddr := "0x0001020304050607080900010203040506070809" + secret := encryptWithEthAddressLabel(t, pk, ethAddr) + + err := EnsureRightLabelOnSecret(wrongPK, secret, ethAddr, "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to verify encrypted value") +} + +func TestEnsureRightLabelOnSecret_BackwardCompatSingleOwner(t *testing.T) { + pk, _ := generateTestKeys(t) + owner := "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" + secret := encryptWithEthAddressLabel(t, pk, owner) + + err := EnsureRightLabelOnSecret(pk, secret, owner, "") + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_LegacySecretReadViaNewFlow(t *testing.T) { + pk, _ := generateTestKeys(t) + workflowOwner := "0x0001020304050607080900010203040506070809" + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + + secret := encryptWithEthAddressLabel(t, pk, workflowOwner) + err := EnsureRightLabelOnSecret(pk, secret, workflowOwner, orgID) + assert.NoError(t, err) +} + +func TestEnsureRightLabelOnSecret_NewSecretReadViaNewFlow(t *testing.T) { + pk, _ := generateTestKeys(t) + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + workflowOwner := "0x0001020304050607080900010203040506070809" + + secret := encryptWithOrgIDLabel(t, pk, orgID) + err := EnsureRightLabelOnSecret(pk, secret, workflowOwner, orgID) + assert.NoError(t, err) +} diff --git a/core/capabilities/vault/vaultutils/labels.go b/core/capabilities/vault/vaultutils/labels.go new file mode 100644 index 00000000000..d7a13fac415 --- /dev/null +++ b/core/capabilities/vault/vaultutils/labels.go @@ -0,0 +1,52 @@ +package vaultutils + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" +) + +// OwnerToLabel converts an owner string to a 32-byte TDH2 ciphertext label. +// It supports two encoding schemes: +// - Ethereum address (detected via common.IsHexAddress): left-padded with 12 zero bytes +// followed by the 20-byte address, matching the legacy workflow_owner encoding. +// - org_id (any non-address string): SHA256 hash of the org_id string. +func OwnerToLabel(owner string) [32]byte { + if common.IsHexAddress(owner) { + var label [32]byte + addr := common.HexToAddress(owner) + copy(label[12:], addr.Bytes()) + return label + } + return sha256.Sum256([]byte(owner)) +} + +// EncryptSecretWithWorkflowOwner encrypts a secret using a TDH2 public key with a label +// derived from a workflow owner's Ethereum address (left-padded to 32 bytes). +func EncryptSecretWithWorkflowOwner(secret string, masterPublicKey *tdh2easy.PublicKey, owner common.Address) (string, error) { + var label [32]byte + copy(label[12:], owner.Bytes()) + return encryptWithLabel(secret, masterPublicKey, label) +} + +// EncryptSecretWithOrgID encrypts a secret using a TDH2 public key with a label +// derived from an org_id (SHA256 hash of the org_id string). +func EncryptSecretWithOrgID(secret string, masterPublicKey *tdh2easy.PublicKey, orgID string) (string, error) { + label := sha256.Sum256([]byte(orgID)) + return encryptWithLabel(secret, masterPublicKey, label) +} + +func encryptWithLabel(secret string, masterPublicKey *tdh2easy.PublicKey, label [32]byte) (string, error) { + cipher, err := tdh2easy.EncryptWithLabel(masterPublicKey, []byte(secret), label) + if err != nil { + return "", fmt.Errorf("failed to encrypt secret: %w", err) + } + cipherBytes, err := cipher.Marshal() + if err != nil { + return "", fmt.Errorf("failed to marshal encrypted secret: %w", err) + } + return hex.EncodeToString(cipherBytes), nil +} diff --git a/core/services/ocr2/plugins/vault/plugin.go b/core/services/ocr2/plugins/vault/plugin.go index d6b30f26be0..a9ab6f99ff7 100644 --- a/core/services/ocr2/plugins/vault/plugin.go +++ b/core/services/ocr2/plugins/vault/plugin.go @@ -575,7 +575,7 @@ func generatePlaintextShare(publicKey *tdh2easy.PublicKey, privateKeyShare *tdh2 } es := hex.EncodeToString(encryptedSecret) - err = vaultcap.EnsureRightLabelOnSecret(publicKey, es, owner) + err = vaultcap.EnsureRightLabelOnSecret(publicKey, es, owner, "") if err != nil { return nil, errors.New("failed to verify label on secret. error: " + err.Error()) } @@ -704,7 +704,7 @@ func (r *ReportingPlugin) observeCreateSecretRequest(ctx context.Context, reader return id, newUserError(ierr.Error()) } - err = vaultcap.EnsureRightLabelOnSecret(r.cfg.PublicKey, secretRequest.EncryptedValue, secretRequest.Id.Owner) + err = vaultcap.EnsureRightLabelOnSecret(r.cfg.PublicKey, secretRequest.EncryptedValue, secretRequest.Id.Owner, "") if err != nil { return id, newUserError("failed to verify ciphertext: " + err.Error()) } diff --git a/system-tests/lib/cre/features/vault/vault.go b/system-tests/lib/cre/features/vault/vault.go index 9eefa2db649..1b7de020fae 100644 --- a/system-tests/lib/cre/features/vault/vault.go +++ b/system-tests/lib/cre/features/vault/vault.go @@ -21,6 +21,7 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" depcontracts "github.com/smartcontractkit/chainlink/deployment/cre/ocr3/ocr3_1/changeset/operations/contracts" coretoml "github.com/smartcontractkit/chainlink/v2/core/config/toml" + "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaultutils" corechainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" vaultprotos "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault" @@ -395,15 +396,5 @@ func EncryptSecret(secret, masterPublicKeyStr string, owner common.Address) (str if err != nil { return "", errors.Wrap(err, "failed to unmarshal master public key") } - var label [32]byte - copy(label[12:], owner.Bytes()) // left-pad with 12 zero - cipher, err := tdh2easy.EncryptWithLabel(&masterPublicKey, []byte(secret), label) - if err != nil { - return "", errors.Wrap(err, "failed to encrypt secret") - } - cipherBytes, err := cipher.Marshal() - if err != nil { - return "", errors.Wrap(err, "failed to marshal encrypted secrets to bytes") - } - return hex.EncodeToString(cipherBytes), nil + return vaultutils.EncryptSecretWithWorkflowOwner(secret, &masterPublicKey, owner) } From 42bfb9360c7a60ec50703f993df9e9e2d84d7d44 Mon Sep 17 00:00:00 2001 From: Prashant Yadav Date: Thu, 26 Mar 2026 00:37:03 -0700 Subject: [PATCH 2/5] fix testifylint: use require.Error for error assertions Made-with: Cursor --- core/capabilities/vault/validator_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/capabilities/vault/validator_test.go b/core/capabilities/vault/validator_test.go index 321bd059aed..de64f24fa48 100644 --- a/core/capabilities/vault/validator_test.go +++ b/core/capabilities/vault/validator_test.go @@ -125,7 +125,7 @@ func TestEnsureRightLabelOnSecret_NeitherMatches(t *testing.T) { secret := encryptWithEthAddressLabel(t, pk, ethAddr) err := EnsureRightLabelOnSecret(pk, secret, wrongAddr, wrongOrgID) - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "does not match any of the provided owner labels") } @@ -135,7 +135,7 @@ func TestEnsureRightLabelOnSecret_BothEmpty(t *testing.T) { secret := encryptWithEthAddressLabel(t, pk, ethAddr) err := EnsureRightLabelOnSecret(pk, secret, "", "") - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "does not match any of the provided owner labels") } @@ -152,7 +152,7 @@ func TestEnsureRightLabelOnSecret_InvalidHexSecret(t *testing.T) { pk, _ := generateTestKeys(t) err := EnsureRightLabelOnSecret(pk, "not-valid-hex!", "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "") - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "failed to decode encrypted value") } @@ -160,7 +160,7 @@ func TestEnsureRightLabelOnSecret_InvalidCiphertext(t *testing.T) { pk, _ := generateTestKeys(t) err := EnsureRightLabelOnSecret(pk, hex.EncodeToString([]byte("garbage")), "0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "") - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "failed to verify encrypted value") } @@ -171,7 +171,7 @@ func TestEnsureRightLabelOnSecret_WrongPublicKey(t *testing.T) { secret := encryptWithEthAddressLabel(t, pk, ethAddr) err := EnsureRightLabelOnSecret(wrongPK, secret, ethAddr, "") - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), "failed to verify encrypted value") } From 43b666037f39605be73fd1c5dcef0626a688c67a Mon Sep 17 00:00:00 2001 From: Prashant Yadav Date: Thu, 26 Mar 2026 00:57:49 -0700 Subject: [PATCH 3/5] fix: use dedicated label functions for workflowOwner and orgID Replace generic OwnerToLabel (which auto-detected type via IsHexAddress) with WorkflowOwnerToLabel and OrgIDToLabel to preserve backward compat with callers that pass non-address strings through HexToAddress. Made-with: Cursor --- core/capabilities/vault/validator.go | 4 +-- core/capabilities/vault/validator_test.go | 34 +++++++++++--------- core/capabilities/vault/vaultutils/labels.go | 27 ++++++++-------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/core/capabilities/vault/validator.go b/core/capabilities/vault/validator.go index 2a15d60c078..52780b22ba2 100644 --- a/core/capabilities/vault/validator.go +++ b/core/capabilities/vault/validator.go @@ -157,14 +157,14 @@ func EnsureRightLabelOnSecret(publicKey *tdh2easy.PublicKey, secret string, work secretLabel := cipherText.Label() if workflowOwner != "" { - expected := vaultutils.OwnerToLabel(workflowOwner) + expected := vaultutils.WorkflowOwnerToLabel(workflowOwner) if secretLabel == expected { return nil } } if orgID != "" { - expected := vaultutils.OwnerToLabel(orgID) + expected := vaultutils.OrgIDToLabel(orgID) if secretLabel == expected { return nil } diff --git a/core/capabilities/vault/validator_test.go b/core/capabilities/vault/validator_test.go index de64f24fa48..cc257750898 100644 --- a/core/capabilities/vault/validator_test.go +++ b/core/capabilities/vault/validator_test.go @@ -34,10 +34,10 @@ func encryptWithOrgIDLabel(t *testing.T, pk *tdh2easy.PublicKey, orgID string) s return encrypted } -func TestOwnerToLabel(t *testing.T) { +func TestWorkflowOwnerToLabel(t *testing.T) { t.Run("ethereum address with 0x prefix", func(t *testing.T) { addr := "0x0001020304050607080900010203040506070809" - label := vaultutils.OwnerToLabel(addr) + label := vaultutils.WorkflowOwnerToLabel(addr) var expected [32]byte copy(expected[12:], common.HexToAddress(addr).Bytes()) @@ -46,35 +46,37 @@ func TestOwnerToLabel(t *testing.T) { t.Run("ethereum address without 0x prefix", func(t *testing.T) { addr := "0001020304050607080900010203040506070809" - label := vaultutils.OwnerToLabel(addr) + label := vaultutils.WorkflowOwnerToLabel(addr) var expected [32]byte copy(expected[12:], common.HexToAddress(addr).Bytes()) assert.Equal(t, expected, label) }) - t.Run("org_id produces SHA256 label", func(t *testing.T) { - orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" - label := vaultutils.OwnerToLabel(orgID) + t.Run("checksummed ethereum address", func(t *testing.T) { + addr := "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" + label := vaultutils.WorkflowOwnerToLabel(addr) - expected := sha256.Sum256([]byte(orgID)) + var expected [32]byte + copy(expected[12:], common.HexToAddress(addr).Bytes()) assert.Equal(t, expected, label) }) +} - t.Run("short string is not an ETH address", func(t *testing.T) { - owner := "my-org-id" - label := vaultutils.OwnerToLabel(owner) +func TestOrgIDToLabel(t *testing.T) { + t.Run("org_id produces SHA256 label", func(t *testing.T) { + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + label := vaultutils.OrgIDToLabel(orgID) - expected := sha256.Sum256([]byte(owner)) + expected := sha256.Sum256([]byte(orgID)) assert.Equal(t, expected, label) }) - t.Run("checksummed ethereum address", func(t *testing.T) { - addr := "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" - label := vaultutils.OwnerToLabel(addr) + t.Run("short string", func(t *testing.T) { + orgID := "my-org-id" + label := vaultutils.OrgIDToLabel(orgID) - var expected [32]byte - copy(expected[12:], common.HexToAddress(addr).Bytes()) + expected := sha256.Sum256([]byte(orgID)) assert.Equal(t, expected, label) }) } diff --git a/core/capabilities/vault/vaultutils/labels.go b/core/capabilities/vault/vaultutils/labels.go index d7a13fac415..9a90b3d76ec 100644 --- a/core/capabilities/vault/vaultutils/labels.go +++ b/core/capabilities/vault/vaultutils/labels.go @@ -9,19 +9,20 @@ import ( "github.com/smartcontractkit/tdh2/go/tdh2/tdh2easy" ) -// OwnerToLabel converts an owner string to a 32-byte TDH2 ciphertext label. -// It supports two encoding schemes: -// - Ethereum address (detected via common.IsHexAddress): left-padded with 12 zero bytes -// followed by the 20-byte address, matching the legacy workflow_owner encoding. -// - org_id (any non-address string): SHA256 hash of the org_id string. -func OwnerToLabel(owner string) [32]byte { - if common.IsHexAddress(owner) { - var label [32]byte - addr := common.HexToAddress(owner) - copy(label[12:], addr.Bytes()) - return label - } - return sha256.Sum256([]byte(owner)) +// WorkflowOwnerToLabel converts a workflow owner string to a 32-byte TDH2 ciphertext +// label using the Ethereum address encoding: 12 zero bytes followed by the 20-byte address. +// This matches the legacy label format used when secrets are encrypted with a workflow owner. +func WorkflowOwnerToLabel(owner string) [32]byte { + var label [32]byte + addr := common.HexToAddress(owner) + copy(label[12:], addr.Bytes()) + return label +} + +// OrgIDToLabel converts an org_id string to a 32-byte TDH2 ciphertext label +// using SHA256 hashing. +func OrgIDToLabel(orgID string) [32]byte { + return sha256.Sum256([]byte(orgID)) } // EncryptSecretWithWorkflowOwner encrypts a secret using a TDH2 public key with a label From 6cd4f8314a858badabccf657dc2be6ee37c99736 Mon Sep 17 00:00:00 2001 From: Prashant Yadav Date: Wed, 1 Apr 2026 00:06:13 -0700 Subject: [PATCH 4/5] gate orgID label check behind VaultOrgIdAsSecretOwnerEnabled limiter Wire workflowOwner and orgID from request-level fields on GetSecretsRequest/CreateSecretsRequest/UpdateSecretsRequest instead of secretRequest.Id.Owner. Only check orgID label when the gate limiter is enabled; rename OrgId -> OrgID per Go naming convention. Made-with: Cursor --- core/services/ocr2/plugins/vault/plugin.go | 36 ++- .../ocr2/plugins/vault/plugin_test.go | 259 +++++++++++++++++- 2 files changed, 283 insertions(+), 12 deletions(-) diff --git a/core/services/ocr2/plugins/vault/plugin.go b/core/services/ocr2/plugins/vault/plugin.go index 3f187215af3..6b6a149cc89 100644 --- a/core/services/ocr2/plugins/vault/plugin.go +++ b/core/services/ocr2/plugins/vault/plugin.go @@ -62,6 +62,7 @@ type ReportingPluginConfig struct { MaxShareLengthBytes limits.BoundLimiter[pkgconfig.Size] MaxRequestBatchSize limits.BoundLimiter[int] MaxBatchSize limits.BoundLimiter[int] + OrgIDAsSecretOwnerEnabled limits.GateLimiter } func NewReportingPluginFactory( @@ -250,6 +251,11 @@ func newReportingPluginConfigLimiters(factory limits.Factory) (*ReportingPluginC return nil, fmt.Errorf("VaultRequestBatchSizeLimit: %w", err) } + orgIDAsSecretOwnerEnabled, err := limits.MakeGateLimiter(factory, cresettings.Default.VaultOrgIdAsSecretOwnerEnabled) + if err != nil { + return nil, fmt.Errorf("VaultOrgIDAsSecretOwnerEnabled: %w", err) + } + return &ReportingPluginConfig{ MaxShareLengthBytes: maxShareLengthBytesLimiter, MaxRequestBatchSize: maxRequestBatchSizeLimiter, @@ -257,6 +263,7 @@ func newReportingPluginConfigLimiters(factory limits.Factory) (*ReportingPluginC MaxIdentifierKeyLengthBytes: maxIdentifierKeyLengthBytesLimiter, MaxIdentifierOwnerLengthBytes: maxIdentifierOwnerLengthBytesLimiter, MaxIdentifierNamespaceLengthBytes: maxIdentifierNamespaceLengthBytesLimiter, + OrgIDAsSecretOwnerEnabled: orgIDAsSecretOwnerEnabled, }, nil } @@ -616,7 +623,7 @@ func (r *ReportingPlugin) observeGetSecrets(ctx context.Context, reader ReadKVSt } resps := []*vaultcommon.SecretResponse{} for _, secretRequest := range tp.Requests { - resp, ierr := r.observeGetSecretsRequest(ctx, reader, secretRequest) + resp, ierr := r.observeGetSecretsRequest(ctx, reader, secretRequest, tp.WorkflowOwner, tp.OrgId) if ierr != nil { logUserErrorAware(r.lggr, "failed to observe get secret request item", ierr, "id", secretRequest.Id) errorMsg := userFacingError(ierr, "failed to handle get secret request") @@ -662,7 +669,7 @@ func (s *share) encryptWithKey(pk string) (string, error) { return hex.EncodeToString(encrypted), nil } -func generatePlaintextShare(publicKey *tdh2easy.PublicKey, privateKeyShare *tdh2easy.PrivateShare, encryptedSecret []byte, owner string) (*share, error) { +func generatePlaintextShare(publicKey *tdh2easy.PublicKey, privateKeyShare *tdh2easy.PrivateShare, encryptedSecret []byte, workflowOwner string, orgID string) (*share, error) { ct := &tdh2easy.Ciphertext{} err := ct.UnmarshalVerify(encryptedSecret, publicKey) if err != nil { @@ -670,7 +677,7 @@ func generatePlaintextShare(publicKey *tdh2easy.PublicKey, privateKeyShare *tdh2 } es := hex.EncodeToString(encryptedSecret) - err = vaultcap.EnsureRightLabelOnSecret(publicKey, es, owner, "") + err = vaultcap.EnsureRightLabelOnSecret(publicKey, es, workflowOwner, orgID) if err != nil { return nil, errors.New("failed to verify label on secret. error: " + err.Error()) } @@ -688,7 +695,7 @@ func generatePlaintextShare(publicKey *tdh2easy.PublicKey, privateKeyShare *tdh2 return &share{data: sb}, nil } -func (r *ReportingPlugin) observeGetSecretsRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.SecretRequest) (*vaultcommon.SecretResponse, error) { +func (r *ReportingPlugin) observeGetSecretsRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.SecretRequest, workflowOwner string, orgID string) (*vaultcommon.SecretResponse, error) { id, err := r.validateSecretIdentifier(ctx, secretRequest.Id) if err != nil { return nil, err @@ -702,7 +709,10 @@ func (r *ReportingPlugin) observeGetSecretsRequest(ctx context.Context, reader R return nil, newUserError("key does not exist") } - sh, err := generatePlaintextShare(r.cfg.PublicKey, r.cfg.PrivateKeyShare, secret.EncryptedSecret, secretRequest.Id.Owner) + if r.cfg.OrgIDAsSecretOwnerEnabled.AllowErr(ctx) != nil { + orgID = "" + } + sh, err := generatePlaintextShare(r.cfg.PublicKey, r.cfg.PrivateKeyShare, secret.EncryptedSecret, workflowOwner, orgID) if err != nil { return nil, err } @@ -757,7 +767,7 @@ func (r *ReportingPlugin) observeCreateSecrets(ctx context.Context, reader ReadK resps := []*vaultcommon.CreateSecretResponse{} for _, sr := range tp.EncryptedSecrets { - validatedID, ierr := r.observeCreateSecretRequest(ctx, reader, sr, requestsCountForID) + validatedID, ierr := r.observeCreateSecretRequest(ctx, reader, sr, requestsCountForID, tp.WorkflowOwner, tp.OrgId) if ierr != nil { logUserErrorAware(l, "failed to handle create secret request item", ierr, "id", sr.Id) errorMsg := userFacingError(ierr, "failed to handle create secret request") @@ -785,7 +795,7 @@ func (r *ReportingPlugin) observeCreateSecrets(ctx context.Context, reader ReadK } } -func (r *ReportingPlugin) observeCreateSecretRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.EncryptedSecret, requestsCountForID map[string]int) (*vaultcommon.SecretIdentifier, error) { +func (r *ReportingPlugin) observeCreateSecretRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.EncryptedSecret, requestsCountForID map[string]int, workflowOwner string, orgID string) (*vaultcommon.SecretIdentifier, error) { id, err := r.validateSecretIdentifier(ctx, secretRequest.Id) if err != nil { return id, err @@ -799,7 +809,10 @@ func (r *ReportingPlugin) observeCreateSecretRequest(ctx context.Context, reader return id, newUserError(ierr.Error()) } - err = vaultcap.EnsureRightLabelOnSecret(r.cfg.PublicKey, secretRequest.EncryptedValue, secretRequest.Id.Owner, "") + if r.cfg.OrgIDAsSecretOwnerEnabled.AllowErr(ctx) != nil { + orgID = "" + } + err = vaultcap.EnsureRightLabelOnSecret(r.cfg.PublicKey, secretRequest.EncryptedValue, workflowOwner, orgID) if err != nil { return id, newUserError("failed to verify ciphertext: " + err.Error()) } @@ -836,7 +849,7 @@ func (r *ReportingPlugin) observeUpdateSecrets(ctx context.Context, reader ReadK resps := []*vaultcommon.UpdateSecretResponse{} for _, sr := range tp.EncryptedSecrets { - validatedID, ierr := r.observeUpdateSecretRequest(ctx, reader, sr, requestsCountForID) + validatedID, ierr := r.observeUpdateSecretRequest(ctx, reader, sr, requestsCountForID, tp.WorkflowOwner, tp.OrgId) if ierr != nil { logUserErrorAware(l, "failed to observe update secret request item", ierr, "id", sr.Id) errorMsg := userFacingError(ierr, "failed to handle update secret request") @@ -864,11 +877,11 @@ func (r *ReportingPlugin) observeUpdateSecrets(ctx context.Context, reader ReadK } } -func (r *ReportingPlugin) observeUpdateSecretRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.EncryptedSecret, requestsCountForID map[string]int) (*vaultcommon.SecretIdentifier, error) { +func (r *ReportingPlugin) observeUpdateSecretRequest(ctx context.Context, reader ReadKVStore, secretRequest *vaultcommon.EncryptedSecret, requestsCountForID map[string]int, workflowOwner string, orgID string) (*vaultcommon.SecretIdentifier, error) { // The checks at this stage are identical since we only check the correctness of the payload // at this stage. Checks that are different between update and create, like whether the secret already exists, // are handled in the StateTransition phase. - return r.observeCreateSecretRequest(ctx, reader, secretRequest, requestsCountForID) + return r.observeCreateSecretRequest(ctx, reader, secretRequest, requestsCountForID, workflowOwner, orgID) } func (r *ReportingPlugin) observeListSecretIdentifiers(ctx context.Context, reader ReadKVStore, req proto.Message, o *vaultcommon.Observation) { @@ -2311,5 +2324,6 @@ func (r *ReportingPlugin) Close() error { r.cfg.MaxShareLengthBytes.Close(), r.cfg.MaxRequestBatchSize.Close(), r.cfg.MaxBatchSize.Close(), + r.cfg.OrgIDAsSecretOwnerEnabled.Close(), ) } diff --git a/core/services/ocr2/plugins/vault/plugin_test.go b/core/services/ocr2/plugins/vault/plugin_test.go index 303b52896ef..6754f8e7163 100644 --- a/core/services/ocr2/plugins/vault/plugin_test.go +++ b/core/services/ocr2/plugins/vault/plugin_test.go @@ -288,6 +288,7 @@ func makeReportingPluginConfig( MaxIdentifierNamespaceLengthBytes: namespaceOwnerLimiter, MaxIdentifierKeyLengthBytes: keyLimiter, MaxRequestBatchSize: requestBatchSizeLimiter, + OrgIDAsSecretOwnerEnabled: limits.NewGateLimiter(false), } } @@ -982,6 +983,7 @@ func TestPlugin_Observation_GetSecretsRequest_FillsInNamespace(t *testing.T) { EncryptionKeys: []string{pks}, }, }, + WorkflowOwner: "owner", } anyp, err := anypb.New(p) require.NoError(t, err) @@ -1013,6 +1015,145 @@ func TestPlugin_Observation_GetSecretsRequest_FillsInNamespace(t *testing.T) { assert.True(t, proto.Equal(batchResp.Responses[0].Id, createdID)) } +func TestPlugin_Observation_GetSecretsRequest_OrgIdLabelAcceptedWhenEnabled(t *testing.T) { + lggr, _ := logger.TestLoggerObserved(t, zapcore.DebugLevel) + store := requests.NewStore[*vaulttypes.Request]() + _, pk, shares, err := tdh2easy.GenerateKeys(1, 3) + require.NoError(t, err) + + cfg := makeReportingPluginConfig(t, 10, pk, shares[0], 1, 1024, 100, 100, 100, 10) + cfg.OrgIDAsSecretOwnerEnabled = limits.NewGateLimiter(true) + + r := &ReportingPlugin{ + lggr: lggr, + store: store, + metrics: newTestMetrics(t), + cfg: cfg, + marshalBlob: mockMarshalBlob, + unmarshalBlob: mockUnmarshalBlob, + } + + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + id := &vaultcommon.SecretIdentifier{ + Owner: orgID, + Namespace: "main", + Key: "my_secret", + } + rdr := &kv{m: make(map[string]response)} + + encrypted, err := vaultutils.EncryptSecretWithOrgID("my-secret-value", pk, orgID) + require.NoError(t, err) + ciphertextBytes, err := hex.DecodeString(encrypted) + require.NoError(t, err) + + err = newTestWriteStore(t, rdr).WriteSecret(t.Context(), id, &vaultcommon.StoredSecret{ + EncryptedSecret: ciphertextBytes, + }) + require.NoError(t, err) + + pubK, _, err := box.GenerateKey(rand.Reader) + require.NoError(t, err) + + p := &vaultcommon.GetSecretsRequest{ + Requests: []*vaultcommon.SecretRequest{ + { + Id: id, + EncryptionKeys: []string{hex.EncodeToString(pubK[:])}, + }, + }, + OrgId: orgID, + } + anyp, err := anypb.New(p) + require.NoError(t, err) + err = newTestWriteStore(t, rdr).WritePendingQueue(t.Context(), + []*vaultcommon.StoredPendingQueueItem{ + {Id: "request-1", Item: anyp}, + }, + ) + require.NoError(t, err) + + data, err := r.Observation(t.Context(), 1, types.AttributedQuery{}, rdr, &blobber{}) + require.NoError(t, err) + + obs := &vaultcommon.Observations{} + err = proto.Unmarshal(data, obs) + require.NoError(t, err) + + require.Len(t, obs.Observations, 1) + batchResp := obs.Observations[0].GetGetSecretsResponse() + require.Len(t, batchResp.Responses, 1) + assert.Empty(t, batchResp.Responses[0].GetError()) +} + +func TestPlugin_Observation_GetSecretsRequest_OrgIdLabelRejectedWhenDisabled(t *testing.T) { + lggr, _ := logger.TestLoggerObserved(t, zapcore.DebugLevel) + store := requests.NewStore[*vaulttypes.Request]() + _, pk, shares, err := tdh2easy.GenerateKeys(1, 3) + require.NoError(t, err) + + cfg := makeReportingPluginConfig(t, 10, pk, shares[0], 1, 1024, 100, 100, 100, 10) + + r := &ReportingPlugin{ + lggr: lggr, + store: store, + metrics: newTestMetrics(t), + cfg: cfg, + marshalBlob: mockMarshalBlob, + unmarshalBlob: mockUnmarshalBlob, + } + + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + id := &vaultcommon.SecretIdentifier{ + Owner: orgID, + Namespace: "main", + Key: "my_secret", + } + rdr := &kv{m: make(map[string]response)} + + encrypted, err := vaultutils.EncryptSecretWithOrgID("my-secret-value", pk, orgID) + require.NoError(t, err) + ciphertextBytes, err := hex.DecodeString(encrypted) + require.NoError(t, err) + + err = newTestWriteStore(t, rdr).WriteSecret(t.Context(), id, &vaultcommon.StoredSecret{ + EncryptedSecret: ciphertextBytes, + }) + require.NoError(t, err) + + pubK, _, err := box.GenerateKey(rand.Reader) + require.NoError(t, err) + + p := &vaultcommon.GetSecretsRequest{ + Requests: []*vaultcommon.SecretRequest{ + { + Id: id, + EncryptionKeys: []string{hex.EncodeToString(pubK[:])}, + }, + }, + OrgId: orgID, + } + anyp, err := anypb.New(p) + require.NoError(t, err) + err = newTestWriteStore(t, rdr).WritePendingQueue(t.Context(), + []*vaultcommon.StoredPendingQueueItem{ + {Id: "request-1", Item: anyp}, + }, + ) + require.NoError(t, err) + + data, err := r.Observation(t.Context(), 1, types.AttributedQuery{}, rdr, &blobber{}) + require.NoError(t, err) + + obs := &vaultcommon.Observations{} + err = proto.Unmarshal(data, obs) + require.NoError(t, err) + + require.Len(t, obs.Observations, 1) + batchResp := obs.Observations[0].GetGetSecretsResponse() + require.Len(t, batchResp.Responses, 1) + assert.Contains(t, batchResp.Responses[0].GetError(), "failed to handle get secret request") +} + func TestPlugin_Observation_GetSecretsRequest_SecretDoesNotExist(t *testing.T) { lggr := logger.TestLogger(t) store := requests.NewStore[*vaulttypes.Request]() @@ -1223,6 +1364,7 @@ func TestPlugin_Observation_GetSecretsRequest_PublicKeyIsInvalid(t *testing.T) { EncryptionKeys: []string{"foo"}, }, }, + WorkflowOwner: "owner", } anyp, err := anypb.New(p) require.NoError(t, err) @@ -1410,6 +1552,7 @@ func TestPlugin_Observation_GetSecretsRequest_Success(t *testing.T) { EncryptionKeys: []string{pks}, }, }, + WorkflowOwner: owner, } anyp, err := anypb.New(p) require.NoError(t, err) @@ -2385,6 +2528,7 @@ func TestPlugin_Observation_CreateSecretsRequest_Success(t *testing.T) { EncryptedValue: hex.EncodeToString(ciphertextBytes), }, }, + WorkflowOwner: "owner", } anyp, err := anypb.New(p) require.NoError(t, err) @@ -2417,6 +2561,119 @@ func TestPlugin_Observation_CreateSecretsRequest_Success(t *testing.T) { assert.Empty(t, resp.GetError()) } +func TestPlugin_Observation_CreateSecretsRequest_OrgIdLabelAcceptedWhenEnabled(t *testing.T) { + lggr := logger.TestLogger(t) + store := requests.NewStore[*vaulttypes.Request]() + _, pk, shares, err := tdh2easy.GenerateKeys(1, 3) + require.NoError(t, err) + + cfg := makeReportingPluginConfig(t, 10, pk, shares[0], 1, 1024, 100, 100, 100, 10) + cfg.OrgIDAsSecretOwnerEnabled = limits.NewGateLimiter(true) + + r := &ReportingPlugin{ + lggr: lggr, + store: store, + metrics: newTestMetrics(t), + marshalBlob: mockMarshalBlob, + unmarshalBlob: mockUnmarshalBlob, + cfg: cfg, + } + + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + id := &vaultcommon.SecretIdentifier{ + Owner: orgID, + Namespace: "main", + Key: "secret", + } + + encrypted, err := vaultutils.EncryptSecretWithOrgID("my secret value", pk, orgID) + require.NoError(t, err) + + rdr := &kv{m: make(map[string]response)} + p := &vaultcommon.CreateSecretsRequest{ + EncryptedSecrets: []*vaultcommon.EncryptedSecret{ + {Id: id, EncryptedValue: encrypted}, + }, + OrgId: orgID, + } + anyp, err := anypb.New(p) + require.NoError(t, err) + err = newTestWriteStore(t, rdr).WritePendingQueue(t.Context(), + []*vaultcommon.StoredPendingQueueItem{ + {Id: "request-1", Item: anyp}, + }, + ) + require.NoError(t, err) + + data, err := r.Observation(t.Context(), 1, types.AttributedQuery{}, rdr, &blobber{}) + require.NoError(t, err) + + obs := &vaultcommon.Observations{} + err = proto.Unmarshal(data, obs) + require.NoError(t, err) + + require.Len(t, obs.Observations, 1) + batchResp := obs.Observations[0].GetCreateSecretsResponse() + require.Len(t, batchResp.Responses, 1) + assert.Empty(t, batchResp.Responses[0].GetError()) +} + +func TestPlugin_Observation_CreateSecretsRequest_OrgIdLabelRejectedWhenDisabled(t *testing.T) { + lggr := logger.TestLogger(t) + store := requests.NewStore[*vaulttypes.Request]() + _, pk, shares, err := tdh2easy.GenerateKeys(1, 3) + require.NoError(t, err) + + cfg := makeReportingPluginConfig(t, 10, pk, shares[0], 1, 1024, 100, 100, 100, 10) + + r := &ReportingPlugin{ + lggr: lggr, + store: store, + metrics: newTestMetrics(t), + marshalBlob: mockMarshalBlob, + unmarshalBlob: mockUnmarshalBlob, + cfg: cfg, + } + + orgID := "org_2xAbCdEfGhIjKlMnOpQrStUvWxYz" + id := &vaultcommon.SecretIdentifier{ + Owner: orgID, + Namespace: "main", + Key: "secret", + } + + encrypted, err := vaultutils.EncryptSecretWithOrgID("my secret value", pk, orgID) + require.NoError(t, err) + + rdr := &kv{m: make(map[string]response)} + p := &vaultcommon.CreateSecretsRequest{ + EncryptedSecrets: []*vaultcommon.EncryptedSecret{ + {Id: id, EncryptedValue: encrypted}, + }, + OrgId: orgID, + } + anyp, err := anypb.New(p) + require.NoError(t, err) + err = newTestWriteStore(t, rdr).WritePendingQueue(t.Context(), + []*vaultcommon.StoredPendingQueueItem{ + {Id: "request-1", Item: anyp}, + }, + ) + require.NoError(t, err) + + data, err := r.Observation(t.Context(), 1, types.AttributedQuery{}, rdr, &blobber{}) + require.NoError(t, err) + + obs := &vaultcommon.Observations{} + err = proto.Unmarshal(data, obs) + require.NoError(t, err) + + require.Len(t, obs.Observations, 1) + batchResp := obs.Observations[0].GetCreateSecretsResponse() + require.Len(t, batchResp.Responses, 1) + assert.Contains(t, batchResp.Responses[0].GetError(), "does not match any of the provided owner labels") +} + func makeEncryptedShares(t *testing.T, ciphertext *tdh2easy.Ciphertext, privateShare *tdh2easy.PrivateShare, keys []string) []*vaultcommon.EncryptedShares { t.Helper() share, err := tdh2easy.Decrypt(ciphertext, privateShare) @@ -6821,7 +7078,7 @@ func TestPlugin_MaxShareSize(t *testing.T) { ctb, err := ciphertext.Marshal() require.NoError(t, err) - share, err := generatePlaintextShare(pk, shares[0], ctb, owner) + share, err := generatePlaintextShare(pk, shares[0], ctb, owner, "") require.NoError(t, err) eds, err := share.encryptWithKey(hex.EncodeToString(recipientPub[:])) From 5542f669f7091c896c6825aafaaede8139b9e57d Mon Sep 17 00:00:00 2001 From: Prashant Yadav Date: Wed, 1 Apr 2026 00:26:26 -0700 Subject: [PATCH 5/5] fix goimports ordering in system-tests vault.go Made-with: Cursor --- system-tests/lib/cre/features/vault/vault.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system-tests/lib/cre/features/vault/vault.go b/system-tests/lib/cre/features/vault/vault.go index b95427a41ea..ce6d9d11530 100644 --- a/system-tests/lib/cre/features/vault/vault.go +++ b/system-tests/lib/cre/features/vault/vault.go @@ -22,8 +22,8 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/ptr" depcontracts "github.com/smartcontractkit/chainlink/deployment/cre/ocr3/ocr3_1/changeset/operations/contracts" - coretoml "github.com/smartcontractkit/chainlink/v2/core/config/toml" "github.com/smartcontractkit/chainlink/v2/core/capabilities/vault/vaultutils" + coretoml "github.com/smartcontractkit/chainlink/v2/core/config/toml" corechainlink "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" vaultprotos "github.com/smartcontractkit/chainlink-common/pkg/capabilities/actions/vault"