Skip to content

fix(e2ee): location message decryption (AAD contentType, isSelf, key pinning) - #218

Open
nezumi0627 wants to merge 1 commit into
evex-dev:mainfrom
nezumi0627:fix/e2ee-location-decrypt
Open

fix(e2ee): location message decryption (AAD contentType, isSelf, key pinning)#218
nezumi0627 wants to merge 1 commit into
evex-dev:mainfrom
nezumi0627:fix/e2ee-location-decrypt

Conversation

@nezumi0627

Copy link
Copy Markdown
Contributor

Summary

decryptE2EELocationMessage could never successfully decrypt a received E2EE location message, for two independent reasons:

1. AAD content type: string "LOCATION" → NaN → 0 (≠ 15)

Incoming messages carry the enum name ("LOCATION") because rename_thrift maps numeric enums back to strings. The encrypt side hardcodes the correct value in the AAD:

// encryptE2EELocationMessage
const aad = this.generateAAD(to, _from, senderKeyId, receiverKeyId, specVersion, 15);

but the decrypt path passed messageObj.contentType straight through as contentType as number. getIntBytes("LOCATION")setInt32(0, NaN) → writes 0, so the decryptor built an AAD with f = 0 while the ciphertext was authenticated with f = 15 — GCM auth verification always failed. The retry block then retried with identical inputs and rethrew.

decryptE2EEDataMessage already handles this correctly (LINETypes.enums.ContentType[messageObj.contentType]); the location path now does the same.

2. isSelf defaulted to true and was never derived

Unlike decryptE2EETextMessage / decryptE2EEDataMessage, the location path never checked _from === profile.mid, and its default parameter was true. The only caller (decryptE2EEMessage) passes no isSelf, so every location message was treated as self-sent: for a received 1:1 message it fetched my own public key via getE2EELocalPublicKey(to=myMid, …) and computed ECDH(myPriv, myPub) instead of ECDH(myPriv, senderPub) — always the wrong shared secret.

It also never pinned the self private key by envelope key id (#88), so it additionally broke on key rotation — exactly what #88 fixed for the text/data paths. The function now mirrors those two:

  • isSelf = false default, derived from from === profile.mid
  • self-key pinned by senderKeyId/receiverKeyId with latest-key fallback
  • peer key looked up as (to, receiverKeyId) when self-sent, (_from, senderKeyId) when received

Testing

  • New location_decrypt.test.ts (same trap-the-primitive approach as key_selection.test.ts), 3 cases:
    • received: AAD contentType is numeric 15, self-key pinned by receiverKeyId, peer pubKey looked up as (_from, senderKeyId)
    • self-sent: isSelf derived from mid, peer pubKey looked up as (to, receiverKeyId)
    • by-id miss falls back to latest key
  • Full suite: deno test --allow-all — 217 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