diff --git a/voice.py b/voice.py index 9cd5031..6e2ca01 100644 --- a/voice.py +++ b/voice.py @@ -302,19 +302,23 @@ class VoiceSession: # 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)) - # Caller keys: use set_shared_key with pre-derived AES (bypasses Rust HKDF). - # Per-participant set_key is NOT called for caller — Rust HKDF may not match EC's JS HKDF. - if self._caller_all_keys: - for idx, base_k in sorted(self._caller_all_keys.items()): - kp.set_shared_key(_hkdf_derive(base_k), idx) - logger.info("Early-set shared_key (pre-derived) for caller indices %s", - list(self._caller_all_keys.keys())) - elif self._caller_key: - kp.set_shared_key(_hkdf_derive(self._caller_key), 0) - logger.info("Early-set shared_key (pre-derived) caller key[0] (%d bytes)", 16) + kp = None + if e2ee_opts is not None: + 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)) + # Caller keys: use set_shared_key with pre-derived AES (bypasses Rust HKDF). + # Per-participant set_key is NOT called for caller — Rust HKDF may not match EC's JS HKDF. + if self._caller_all_keys: + for idx, base_k in sorted(self._caller_all_keys.items()): + kp.set_shared_key(_hkdf_derive(base_k), idx) + logger.info("Early-set shared_key (pre-derived) for caller indices %s", + list(self._caller_all_keys.keys())) + elif self._caller_key: + kp.set_shared_key(_hkdf_derive(self._caller_key), 0) + logger.info("Early-set shared_key (pre-derived) caller key[0] (%d bytes)", 16) + else: + logger.info("E2EE disabled (diagnostic mode) — skipping key setup") # 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 @@ -353,7 +357,7 @@ class VoiceSession: # Set shared_key with pre-derived AES key for caller decryption. # NOT using set_key() for caller — Rust HKDF may produce different result than EC's JS HKDF. # set_shared_key() stores key raw (no KDF applied) — we pre-derive in Python. - if self._caller_all_keys: + if kp is not None and self._caller_all_keys: try: for idx, base_k in sorted(self._caller_all_keys.items()): derived = _hkdf_derive(base_k) @@ -361,7 +365,7 @@ class VoiceSession: logger.info("Set shared_key (pre-derived)[%d] (%d bytes)", idx, len(derived)) except Exception as e: logger.warning("Failed to set caller shared_key: %s", e) - elif not self._caller_all_keys: + elif e2ee_opts is not None and not self._caller_all_keys: logger.warning("No caller E2EE keys — incoming audio will be silence") if remote_identity: