Skip to content

fix(e2ee): scope public-key cache by mid to prevent cross-contact key mixups - #219

Open
nezumi0627 wants to merge 1 commit into
evex-dev:mainfrom
nezumi0627:fix/e2ee-publickey-cache-scope
Open

fix(e2ee): scope public-key cache by mid to prevent cross-contact key mixups#219
nezumi0627 wants to merge 1 commit into
evex-dev:mainfrom
nezumi0627:fix/e2ee-publickey-cache-scope

Conversation

@nezumi0627

Copy link
Copy Markdown
Contributor

Summary

The per-user E2EE public key cache was keyed only by keyId (e2eePublicKeys:${keyId}), not by mid.

LINE E2EE key ids are small per-account counters — each account's first registered key typically gets id 1 or 2. With two or more E2EE-capable contacts, the cache slots collide:

  • Decrypting a message from contact B returned contact A's cached public key (no RPC was even attempted for B), producing a wrong ECDH shared secret and a GCM auth failure.
  • Each successful negotiateE2EEPublicKey overwrote the shared slot, so two contacts with the same keyId continuously evicted each other's keys.
  • The group path is affected too, since the creator key is fetched through the same function.

This also masked itself on the send side: encryptE2EEMessage calls negotiateE2EEPublicKey directly and never touches this cache.

Fix

Cache key becomes e2eePublicKeys:${mid}:${keyId} (read + write). Old keyId-only entries are simply never hit again and get re-negotiated once per contact — no migration needed.

Also fixes a latent always-throw path in the same block: when callers omitted keyId, negotiation succeeded but receiverKeyId == undefined was always false, throwing `E2EE key id undefined not found…` and wasting an RPC. An omitted keyId now means "accept whatever the server negotiated" (cached under its actual id).

Testing

New publickey_cache.test.ts:

  1. Two contacts both with keyId 1: each lookup negotiates and returns that contact's own key material; repeated lookups still hit the per-mid cache (no extra RPC).
  2. Omitted keyId: previously threw InternalError; now returns the negotiated key.

Full suite: deno test --allow-all — 216 passed, 0 failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant