fix(matrix): handle SAS device verification
This commit is contained in:
@@ -50,7 +50,15 @@ class _FakeAsyncClient:
|
||||
self.stop_sync_forever_called = False
|
||||
self.join_calls: list[str] = []
|
||||
self.callbacks: list[tuple[object, object]] = []
|
||||
self.to_device_callbacks: list[tuple[object, object]] = []
|
||||
self.response_callbacks: list[tuple[object, object]] = []
|
||||
self.key_verifications: dict[str, object] = {}
|
||||
self.accept_key_verification_calls: list[str] = []
|
||||
self.confirm_short_auth_string_calls: list[str] = []
|
||||
self.to_device_calls: list[object] = []
|
||||
self.accept_key_verification_response: object | None = None
|
||||
self.confirm_short_auth_string_response: object | None = None
|
||||
self.to_device_response: object | None = None
|
||||
self.rooms: dict[str, object] = {}
|
||||
self.room_send_calls: list[dict[str, object]] = []
|
||||
self.typing_calls: list[tuple[str, bool, int]] = []
|
||||
@@ -70,6 +78,9 @@ class _FakeAsyncClient:
|
||||
def add_event_callback(self, callback, event_type) -> None:
|
||||
self.callbacks.append((callback, event_type))
|
||||
|
||||
def add_to_device_callback(self, callback, event_type) -> None:
|
||||
self.to_device_callbacks.append((callback, event_type))
|
||||
|
||||
def add_response_callback(self, callback, response_type) -> None:
|
||||
self.response_callbacks.append((callback, response_type))
|
||||
|
||||
@@ -82,6 +93,18 @@ class _FakeAsyncClient:
|
||||
async def join(self, room_id: str) -> None:
|
||||
self.join_calls.append(room_id)
|
||||
|
||||
async def accept_key_verification(self, transaction_id: str):
|
||||
self.accept_key_verification_calls.append(transaction_id)
|
||||
return self.accept_key_verification_response
|
||||
|
||||
async def confirm_short_auth_string(self, transaction_id: str):
|
||||
self.confirm_short_auth_string_calls.append(transaction_id)
|
||||
return self.confirm_short_auth_string_response
|
||||
|
||||
async def to_device(self, message):
|
||||
self.to_device_calls.append(message)
|
||||
return self.to_device_response
|
||||
|
||||
async def room_send(
|
||||
self,
|
||||
room_id: str,
|
||||
@@ -166,6 +189,61 @@ class _FakeAsyncClient:
|
||||
return None
|
||||
|
||||
|
||||
class _FakeSas:
|
||||
def __init__(self) -> None:
|
||||
self.share_key_called = False
|
||||
self.get_mac_called = False
|
||||
|
||||
def share_key(self):
|
||||
self.share_key_called = True
|
||||
return {"type": "share_key"}
|
||||
|
||||
def get_mac(self):
|
||||
self.get_mac_called = True
|
||||
return {"type": "mac"}
|
||||
|
||||
|
||||
class _FakeKeyVerificationStart:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
sender: str = "@alice:matrix.org",
|
||||
transaction_id: str = "tx1",
|
||||
short_authentication_string: list[str] | None = None,
|
||||
) -> None:
|
||||
self.sender = sender
|
||||
self.transaction_id = transaction_id
|
||||
self.short_authentication_string = short_authentication_string or ["emoji"]
|
||||
|
||||
|
||||
class _FakeKeyVerificationKey:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
sender: str = "@alice:matrix.org",
|
||||
transaction_id: str = "tx1",
|
||||
) -> None:
|
||||
self.sender = sender
|
||||
self.transaction_id = transaction_id
|
||||
|
||||
|
||||
class _FakeKeyVerificationMac:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
sender: str = "@alice:matrix.org",
|
||||
transaction_id: str = "tx1",
|
||||
) -> None:
|
||||
self.sender = sender
|
||||
self.transaction_id = transaction_id
|
||||
|
||||
|
||||
def _patch_key_verification_events(monkeypatch) -> None:
|
||||
monkeypatch.setattr(matrix_module, "KeyVerificationStart", _FakeKeyVerificationStart)
|
||||
monkeypatch.setattr(matrix_module, "KeyVerificationKey", _FakeKeyVerificationKey)
|
||||
monkeypatch.setattr(matrix_module, "KeyVerificationMac", _FakeKeyVerificationMac)
|
||||
|
||||
|
||||
def _make_config(**kwargs) -> MatrixConfig:
|
||||
kwargs.setdefault("allow_from", ["*"])
|
||||
return MatrixConfig(
|
||||
@@ -209,6 +287,7 @@ async def test_start_skips_load_store_when_device_id_missing(
|
||||
assert clients[0].config.encryption_enabled is True
|
||||
assert clients[0].load_store_called is False
|
||||
assert len(clients[0].callbacks) == 3
|
||||
assert clients[0].to_device_callbacks == []
|
||||
assert len(clients[0].response_callbacks) == 3
|
||||
|
||||
await channel.stop()
|
||||
@@ -227,6 +306,119 @@ async def test_register_event_callbacks_uses_media_base_filter() -> None:
|
||||
assert client.callbacks[1][1] == matrix_module.MATRIX_MEDIA_EVENT_FILTER
|
||||
|
||||
|
||||
def test_register_to_device_callbacks_when_sas_verification_enabled() -> None:
|
||||
channel = MatrixChannel(_make_config(sas_verification=True), MessageBus())
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
channel.client = client
|
||||
|
||||
channel._register_to_device_callbacks()
|
||||
|
||||
assert client.to_device_callbacks == [
|
||||
(channel._on_key_verification_event, (matrix_module.KeyVerificationEvent,))
|
||||
]
|
||||
|
||||
|
||||
def test_register_to_device_callbacks_skips_when_e2ee_disabled() -> None:
|
||||
channel = MatrixChannel(
|
||||
_make_config(e2ee_enabled=False, sas_verification=True),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
channel.client = client
|
||||
|
||||
channel._register_to_device_callbacks()
|
||||
|
||||
assert client.to_device_callbacks == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sas_verification_start_accepts_allowed_sender(monkeypatch) -> None:
|
||||
_patch_key_verification_events(monkeypatch)
|
||||
channel = MatrixChannel(
|
||||
_make_config(allow_from=["@alice:matrix.org"], sas_verification=True),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
sas = _FakeSas()
|
||||
client.key_verifications["tx1"] = sas
|
||||
channel.client = client
|
||||
|
||||
await channel._handle_key_verification_event(_FakeKeyVerificationStart())
|
||||
|
||||
assert client.accept_key_verification_calls == ["tx1"]
|
||||
assert sas.share_key_called is True
|
||||
assert client.to_device_calls == [{"type": "share_key"}]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sas_verification_ignores_denied_sender(monkeypatch) -> None:
|
||||
_patch_key_verification_events(monkeypatch)
|
||||
channel = MatrixChannel(
|
||||
_make_config(allow_from=["@alice:matrix.org"], sas_verification=True),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
client.key_verifications["tx1"] = _FakeSas()
|
||||
channel.client = client
|
||||
|
||||
await channel._handle_key_verification_event(
|
||||
_FakeKeyVerificationStart(sender="@mallory:matrix.org")
|
||||
)
|
||||
|
||||
assert client.accept_key_verification_calls == []
|
||||
assert client.to_device_calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sas_verification_ignores_when_disabled(monkeypatch) -> None:
|
||||
_patch_key_verification_events(monkeypatch)
|
||||
channel = MatrixChannel(
|
||||
_make_config(allow_from=["@alice:matrix.org"], sas_verification=False),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
client.key_verifications["tx1"] = _FakeSas()
|
||||
channel.client = client
|
||||
|
||||
await channel._handle_key_verification_event(_FakeKeyVerificationStart())
|
||||
|
||||
assert client.accept_key_verification_calls == []
|
||||
assert client.to_device_calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sas_verification_key_confirms_allowed_sender(monkeypatch) -> None:
|
||||
_patch_key_verification_events(monkeypatch)
|
||||
channel = MatrixChannel(
|
||||
_make_config(allow_from=["@alice:matrix.org"], sas_verification=True),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
channel.client = client
|
||||
|
||||
await channel._handle_key_verification_event(_FakeKeyVerificationKey())
|
||||
|
||||
assert client.confirm_short_auth_string_calls == ["tx1"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sas_verification_mac_sends_mac_for_allowed_sender(monkeypatch) -> None:
|
||||
_patch_key_verification_events(monkeypatch)
|
||||
channel = MatrixChannel(
|
||||
_make_config(allow_from=["@alice:matrix.org"], sas_verification=True),
|
||||
MessageBus(),
|
||||
)
|
||||
client = _FakeAsyncClient("", "", "", None)
|
||||
sas = _FakeSas()
|
||||
client.key_verifications["tx1"] = sas
|
||||
channel.client = client
|
||||
|
||||
await channel._handle_key_verification_event(_FakeKeyVerificationMac())
|
||||
|
||||
assert sas.get_mac_called is True
|
||||
assert client.to_device_calls == [{"type": "mac"}]
|
||||
|
||||
|
||||
def test_media_event_filter_does_not_match_text_events() -> None:
|
||||
assert not issubclass(matrix_module.RoomMessageText, matrix_module.MATRIX_MEDIA_EVENT_FILTER)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user