Skip to content

Commit 33a034a

Browse files
authored
Add support for MSC4190 (#175)
1 parent bba5542 commit 33a034a

File tree

3 files changed

+42
-11
lines changed

3 files changed

+42
-11
lines changed

mautrix/bridge/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def do_update(self, helper: ConfigUpdateHelper) -> None:
143143
copy("bridge.encryption.default")
144144
copy("bridge.encryption.require")
145145
copy("bridge.encryption.appservice")
146+
copy("bridge.encryption.msc4190")
146147
copy("bridge.encryption.delete_keys.delete_outbound_on_ack")
147148
copy("bridge.encryption.delete_keys.dont_store_outbound")
148149
copy("bridge.encryption.delete_keys.ratchet_on_decrypt")
@@ -241,3 +242,6 @@ def generate_registration(self) -> None:
241242
if self["appservice.ephemeral_events"]:
242243
self._registration["de.sorunome.msc2409.push_ephemeral"] = True
243244
self._registration["push_ephemeral"] = True
245+
246+
if self["bridge.encryption.msc4190"]:
247+
self._registration["io.element.msc4190"] = True

mautrix/bridge/e2ee.py

+23-11
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class EncryptionManager:
5757
appservice_mode: bool
5858
periodically_delete_expired_keys: bool
5959
delete_outdated_inbound: bool
60+
msc4190: bool
6061

6162
bridge: br.Bridge
6263
az: AppService
@@ -108,6 +109,7 @@ def __init__(
108109
self.crypto.send_keys_min_trust = TrustState.parse(verification_levels["receive"])
109110
self.key_sharing_enabled = bridge.config["bridge.encryption.allow_key_sharing"]
110111
self.appservice_mode = bridge.config["bridge.encryption.appservice"]
112+
self.msc4190 = bridge.config["bridge.encryption.msc4190"]
111113
if self.appservice_mode:
112114
self.az.otk_handler = self.crypto.handle_as_otk_counts
113115
self.az.device_list_handler = self.crypto.handle_as_device_lists
@@ -246,7 +248,7 @@ async def decrypt(self, evt: EncryptedEvent, wait_session_timeout: int = 5) -> M
246248

247249
async def start(self) -> None:
248250
flows = await self.client.get_login_flows()
249-
if not flows.supports_type(LoginType.APPSERVICE):
251+
if not self.msc4190 and not flows.supports_type(LoginType.APPSERVICE):
250252
self.log.critical(
251253
"Encryption enabled in config, but homeserver does not support appservice login"
252254
)
@@ -261,16 +263,26 @@ async def start(self) -> None:
261263
device_id = await self.crypto_store.get_device_id()
262264
if device_id:
263265
self.log.debug(f"Found device ID in database: {device_id}")
264-
# We set the API token to the AS token here to authenticate the appservice login
265-
# It'll get overridden after the login
266-
self.client.api.token = self.az.as_token
267-
await self.client.login(
268-
login_type=LoginType.APPSERVICE,
269-
device_name=self.device_name,
270-
device_id=device_id,
271-
store_access_token=True,
272-
update_hs_url=False,
273-
)
266+
267+
if self.msc4190:
268+
if not device_id:
269+
self.log.debug("Creating bot device with MSC4190")
270+
self.client.api.token = self.az.as_token
271+
await self.client.create_device_msc4190(
272+
device_id=device_id, initial_display_name=self.device_name
273+
)
274+
else:
275+
# We set the API token to the AS token here to authenticate the appservice login
276+
# It'll get overridden after the login
277+
self.client.api.token = self.az.as_token
278+
await self.client.login(
279+
login_type=LoginType.APPSERVICE,
280+
device_name=self.device_name,
281+
device_id=device_id,
282+
store_access_token=True,
283+
update_hs_url=False,
284+
)
285+
274286
await self.crypto.load()
275287
if not device_id:
276288
await self.crypto_store.put_device_id(self.client.device_id)

mautrix/client/api/authentication.py

+15
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
from __future__ import annotations
77

8+
import secrets
9+
810
from mautrix.api import Method, Path
911
from mautrix.errors import MatrixResponseError
1012
from mautrix.types import (
@@ -117,6 +119,19 @@ async def login(
117119
self.api.base_url = base_url.rstrip("/")
118120
return resp_data
119121

122+
async def create_device_msc4190(self, device_id: str, initial_display_name: str) -> None:
123+
"""
124+
Create a Device for a user of the homeserver using appservice interface defined in MSC4190
125+
"""
126+
if len(device_id) == 0:
127+
device_id = DeviceID(secrets.token_urlsafe(10))
128+
self.api.as_user_id = self.mxid
129+
await self.api.request(
130+
Method.PUT, Path.v3.devices[device_id], {"display_name": initial_display_name}
131+
)
132+
self.api.as_device_id = device_id
133+
self.device_id = device_id
134+
120135
async def logout(self, clear_access_token: bool = True) -> None:
121136
"""
122137
Invalidates an existing access token, so that it can no longer be used for authorization.

0 commit comments

Comments
 (0)