fix: republish caller E2EE key as shared key, fallback to no-E2EE
Bot now publishes the same key as the caller so both sides can decrypt. Falls back to no-encryption if no caller key received. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
17
bot.py
17
bot.py
@@ -448,14 +448,12 @@ class Bot:
|
|||||||
model = self.room_models.get(room_id, DEFAULT_MODEL)
|
model = self.room_models.get(room_id, DEFAULT_MODEL)
|
||||||
caller_device_id = content.get("device_id", "")
|
caller_device_id = content.get("device_id", "")
|
||||||
|
|
||||||
# Generate our E2EE key and publish it as a timeline event FIRST.
|
# Publish a placeholder key first to trigger Element Call
|
||||||
# Element Call only shares its key after seeing ours.
|
# to share its key with us. We'll republish the real shared
|
||||||
|
# key once we receive the caller's key.
|
||||||
import secrets
|
import secrets
|
||||||
our_key = secrets.token_bytes(32)
|
placeholder_key = secrets.token_bytes(16)
|
||||||
await self._publish_encryption_key(room_id, our_key)
|
await self._publish_encryption_key(room_id, placeholder_key)
|
||||||
|
|
||||||
# Now check timeline for caller's key (they may have published before us)
|
|
||||||
caller_key = await self._get_call_encryption_key(room_id, event.sender, caller_device_id)
|
|
||||||
|
|
||||||
vs = VoiceSession(
|
vs = VoiceSession(
|
||||||
nio_client=self.client,
|
nio_client=self.client,
|
||||||
@@ -463,7 +461,12 @@ class Bot:
|
|||||||
device_id=BOT_DEVICE_ID,
|
device_id=BOT_DEVICE_ID,
|
||||||
lk_url=LK_URL,
|
lk_url=LK_URL,
|
||||||
model=model,
|
model=model,
|
||||||
|
publish_key_cb=lambda key: asyncio.ensure_future(
|
||||||
|
self._publish_encryption_key(room_id, key)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check timeline for caller's key
|
||||||
|
caller_key = await self._get_call_encryption_key(room_id, event.sender, caller_device_id)
|
||||||
if caller_key:
|
if caller_key:
|
||||||
vs.on_encryption_key(event.sender, caller_device_id, caller_key, 0)
|
vs.on_encryption_key(event.sender, caller_device_id, caller_key, 0)
|
||||||
|
|
||||||
|
|||||||
17
voice.py
17
voice.py
@@ -83,7 +83,8 @@ def _build_e2ee_options(shared_key: bytes) -> rtc.E2EEOptions:
|
|||||||
|
|
||||||
|
|
||||||
class VoiceSession:
|
class VoiceSession:
|
||||||
def __init__(self, nio_client, room_id, device_id, lk_url, model="claude-sonnet"):
|
def __init__(self, nio_client, room_id, device_id, lk_url, model="claude-sonnet",
|
||||||
|
publish_key_cb=None):
|
||||||
self.nio_client = nio_client
|
self.nio_client = nio_client
|
||||||
self.room_id = room_id
|
self.room_id = room_id
|
||||||
self.device_id = device_id
|
self.device_id = device_id
|
||||||
@@ -94,6 +95,7 @@ class VoiceSession:
|
|||||||
self._task = None
|
self._task = None
|
||||||
self._http_session = None
|
self._http_session = None
|
||||||
self._e2ee_key: bytes | None = None
|
self._e2ee_key: bytes | None = None
|
||||||
|
self._publish_key_cb = publish_key_cb
|
||||||
|
|
||||||
def on_encryption_key(self, sender, device_id, key, index):
|
def on_encryption_key(self, sender, device_id, key, index):
|
||||||
"""Receive E2EE key from Element Call participant."""
|
"""Receive E2EE key from Element Call participant."""
|
||||||
@@ -186,14 +188,15 @@ class VoiceSession:
|
|||||||
|
|
||||||
if self._e2ee_key:
|
if self._e2ee_key:
|
||||||
shared_key = self._e2ee_key
|
shared_key = self._e2ee_key
|
||||||
logger.info("Using E2EE key (%d bytes)", len(shared_key))
|
logger.info("Using caller E2EE key (%d bytes)", len(shared_key))
|
||||||
|
# Republish caller's key as our own so both sides share the same key
|
||||||
|
if self._publish_key_cb:
|
||||||
|
self._publish_key_cb(shared_key)
|
||||||
e2ee_opts = _build_e2ee_options(shared_key)
|
e2ee_opts = _build_e2ee_options(shared_key)
|
||||||
else:
|
else:
|
||||||
# Generate our own key as fallback — bot.py already published one
|
# No key received — connect WITHOUT E2EE so at least audio works
|
||||||
import secrets
|
logger.warning("No caller E2EE key received — connecting WITHOUT encryption")
|
||||||
shared_key = secrets.token_bytes(32)
|
e2ee_opts = None
|
||||||
logger.warning("No caller E2EE key received — using generated key")
|
|
||||||
e2ee_opts = _build_e2ee_options(shared_key)
|
|
||||||
|
|
||||||
room_opts = rtc.RoomOptions(e2ee=e2ee_opts)
|
room_opts = rtc.RoomOptions(e2ee=e2ee_opts)
|
||||||
self.lk_room = rtc.Room()
|
self.lk_room = rtc.Room()
|
||||||
|
|||||||
Reference in New Issue
Block a user