diff --git a/voice.py b/voice.py index a31ddcd..c5b4027 100644 --- a/voice.py +++ b/voice.py @@ -495,15 +495,32 @@ class VoiceSession: Store-only: keys are applied in on_track_subscribed() / on_e2ee_state_changed() where the frame cryptor is guaranteed to exist. If the track is already subscribed (late key arrival / rotation), set the key immediately. + + Element Call may rotate keys by re-sending index 0 with a new value + (e.g. when screen share starts). The frame cryptor caches derived AES + keys per index, so we use an internal incrementing index to force + re-derivation. """ if not key: return if not self._caller_key: self._caller_key = key self._caller_identity = f"{sender}:{device_id}" - self._caller_all_keys[index] = key - logger.info("E2EE key received from %s:%s (index=%d, %d bytes, raw=%s)", - sender, device_id, index, len(key), key.hex()) + # Detect same-index key rotation: if index 0 changes value, use next internal index + old_key = self._caller_all_keys.get(index) + if old_key and old_key != key: + # Key rotated at same Element Call index — use next available internal index + internal_idx = max(self._caller_all_keys.keys()) + 1 + logger.info("Key rotation detected: index %d changed value, mapping to internal index %d", + index, internal_idx) + self._caller_all_keys[internal_idx] = key + self._caller_key = key + effective_index = internal_idx + else: + self._caller_all_keys[index] = key + effective_index = index + logger.info("E2EE key received from %s:%s (index=%d, effective=%d, %d bytes, raw=%s)", + sender, device_id, index, effective_index, len(key), key.hex()) # Late key arrival: if track already subscribed, frame cryptor exists — set key now. if self.lk_room and self._caller_identity: caller_lk_id = self._caller_identity @@ -515,8 +532,11 @@ class VoiceSession: if has_subscribed: try: kp = self.lk_room.e2ee_manager.key_provider - _derive_and_set_key(kp, p.identity, key, index) - logger.info("Late key[%d] set for %s", index, p.identity) + # Set ALL keys (old + new) so cryptor can try both + for idx, k in sorted(self._caller_all_keys.items()): + _derive_and_set_key(kp, p.identity, k, idx) + logger.info("Late key rotation: set %d keys for %s", + len(self._caller_all_keys), p.identity) except Exception as e: logger.warning("Late key set_key failed: %s", e) break