diff --git a/voice.py b/voice.py index 340494f..47e81ad 100644 --- a/voice.py +++ b/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():