apache / apache/pulsar-client-go

Producer encryption key metadata is never refreshed after first message

Open
#1,523 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
745
Forks
389
Avg merge
3d 20h
Merged PRs (30d)
3

Description

## Expected behavior

When `KeyReader.PublicKey()` returns updated metadata for a key name (e.g., after key rotation), subsequent `Encrypt` calls should use the new metadata.

## Actual behavior

`DefaultMessageCrypto.Encrypt` caches the `EncryptionKeyInfo` (including metadata) on the first call for each `keyName` in `encryptedDataKeyMap`. The guard at line 153 (`if _, ok := d.encryptedDataKeyMap.Load(keyName); !ok`) prevents `addPublicKeyCipher` from being called again, so metadata is frozen for the producer's lifetime.

Long-lived producers stamp stale key-id metadata onto every message. After key rotation, consumers request private keys that no longer exist.

## Steps to reproduce

1. Create a `KeyReader` returning `Metadata{"key-id": "uuid-A"}` for key name `"latest"`
2. Call `Encrypt(["latest"], ...)` — metadata has `uuid-A` ✓
3. Update `KeyReader` to return `Metadata{"key-id": "uuid-B"}`
4. Call `Encrypt(["latest"], ...)` again
5. **Expected:** `uuid-B` / **Actual:** still `uuid-A`

Failing test

```go
func TestKeyRotationMetadataNotRefreshed(t *testing.T) {
keyName := "latest"
keyIDs := []string{"uuid-A", "uuid-B"}
keyReader := newRotatingKeyReader(t, keyIDs) // returns keyIDs[callCount % len] on each PublicKey call

msgCrypto, _ := NewDefaultMessageCrypto("test", true, log.DefaultNopLogger())

// First encrypt: captures uuid-A
msg1 := &pb.MessageMetadata{}
msgCrypto.Encrypt([]string{keyName}, keyReader, NewMessageMetadataSupplier(msg1), []byte("m1"))
keyID1 := msg1.EncryptionKeys[0].Metadata // uuid-A ✓

// KeyReader now returns uuid-B, but Encrypt won't call it again
msg2 := &pb.MessageMetadata{}
msgCrypto.Encrypt([]string{keyName}, keyReader, NewMessageMetadataSupplier(msg2), []byte("m2"))
keyID2 := msg2.EncryptionKeys[0].Metadata

// FAILS: keyID2 == "uuid-A" (stale), expected "uuid-B"
assert.Equal(t, "uuid-B", keyID2)
}
```

## System configuration

- pulsar-client-go: v0.19.0, v0.20.0, master
- Go/OS: any

---

I plan to open a PR with a fix. Thanks!

Contributor guide

Open the contributing guide

Research direction

Start by locating DefaultMessageCrypto.Encrypt, encryptedDataKeyMap, and addPublicKeyCipher around the guard described at line 153. Reproduce the supplied TestKeyRotationMetadataNotRefreshed case with the rotating KeyReader, then run the relevant Go tests and verify that the second Encrypt records uuid-B rather than stale uuid-A metadata.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.