fix(voice): set E2EE keys immediately after connect, before rotation wait
Root cause: caller track subscribed during 2s rotation wait creates a frame cryptor with no key → DEC_FAILED state → all incoming frames dropped. Setting the key after the wait doesn't recover the cryptor. Fix: set bot + caller keys immediately after lk_room.connect(), using the Matrix-provided caller identity. The post-rotation and post-find-remote key updates remain as belt+suspenders. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
25
voice.py
25
voice.py
@@ -257,6 +257,23 @@ class VoiceSession:
|
||||
logger.info("Connected (E2EE=HKDF), remote=%d",
|
||||
len(self.lk_room.remote_participants))
|
||||
|
||||
# *** FIX: Set keys immediately after connect — BEFORE the rotation wait.
|
||||
# The caller's track is subscribed during the wait; if no key is set when
|
||||
# the frame cryptor is first created it enters DEC_FAILED and drops all frames
|
||||
# even after the key is set later.
|
||||
kp = self.lk_room.e2ee_manager.key_provider
|
||||
kp.set_key(bot_identity, self._bot_key, 0)
|
||||
logger.info("Set bot raw key for %s (%d bytes)", bot_identity, len(self._bot_key))
|
||||
if self._caller_identity and self._caller_all_keys:
|
||||
for idx, base_k in sorted(self._caller_all_keys.items()):
|
||||
kp.set_key(self._caller_identity, base_k, idx)
|
||||
logger.info("Early-set caller raw keys %s for %s",
|
||||
list(self._caller_all_keys.keys()), self._caller_identity)
|
||||
elif self._caller_key and self._caller_identity:
|
||||
kp.set_key(self._caller_identity, self._caller_key, 0)
|
||||
logger.info("Early-set caller raw key[0] for %s (%d bytes)",
|
||||
self._caller_identity, len(self._caller_key))
|
||||
|
||||
# Element Call rotates its encryption key when bot joins the LiveKit room.
|
||||
# EC sends the new key via Matrix (Megolm-encrypted); nio sync will decrypt it
|
||||
# and call on_encryption_key(), which updates self._caller_all_keys.
|
||||
@@ -276,14 +293,6 @@ class VoiceSession:
|
||||
else:
|
||||
logger.warning("No key rotation after 2s — using pre-join key[%d]", pre_max_idx)
|
||||
|
||||
# Set per-participant keys via key provider — pass raw base keys.
|
||||
# KDF_HKDF=1: Rust FFI applies HKDF(base_key, ratchetSalt, ...) internally.
|
||||
kp = self.lk_room.e2ee_manager.key_provider
|
||||
|
||||
# Bot's own key — raw base key, Rust FFI derives AES frame key via HKDF
|
||||
kp.set_key(bot_identity, self._bot_key, 0)
|
||||
logger.info("Set bot raw key for %s (%d bytes)", bot_identity, len(self._bot_key))
|
||||
|
||||
# Find the remote participant, wait up to 10s if not yet connected
|
||||
remote_identity = None
|
||||
for p in self.lk_room.remote_participants.values():
|
||||
|
||||
Reference in New Issue
Block a user