Skip to content

Commit

Permalink
Merge branch 'matrix-org:master' into decrypt-initial-dm
Browse files Browse the repository at this point in the history
  • Loading branch information
codemonium authored Oct 30, 2024
2 parents 313a5d5 + 6d88b8a commit a82652f
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 128 deletions.
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Installing pantalaimon works like usually with python packages:

python setup.py install

or you can use `pip` and install it with:
```
pip install .[ui]
```

It is recommended that you create a virtual environment first or install dependencies
via your package manager. They are usually found with `python-<package-name>`.

Pantalaimon can also be found on pypi:

pip install pantalaimon
Expand Down Expand Up @@ -111,7 +119,7 @@ specifies one or more homeservers for pantalaimon to connect to.
A minimal pantalaimon configuration looks like this:
```dosini
[local-matrix]
Homeserver = https://localhost:8448
Homeserver = https://localhost:443
ListenAddress = localhost
ListenPort = 8009
```
Expand Down Expand Up @@ -140,3 +148,41 @@ To control the daemon an interactive utility is provided in the form of
`panctl` can be used to verify, blacklist or ignore devices, import or export
session keys, or to introspect devices of users that we share encrypted rooms
with.

### Setup
This is all coming from an excellent comment that you can find [here](https://github.com/matrix-org/pantalaimon/issues/154#issuecomment-1951591191).



1) Ensure you have an OS keyring installed. In my case I installed `gnome-keyring`. You may also want a GUI like `seahorse` to inspect the keyring. (pantalaimon will work without a keyring but your client will have to log in with the password every time `pantalaimon` is restarted, instead of being able to reuse the access token from the previous successful login.)

2) In case you have prior attempts, clean the slate by deleting the `~/.local/share/pantalaimon` directory.

3) Start `pantalaimon`.

4) Connect a client to the `ListenAddress:ListenPort` you specified in `pantalaimon.conf`, eg to `127.0.0.1:8009`, using the same username and password you would've used to login to your homeserver directly.

5) The login should succeed, but at this point all encrypted messages will fail to decrypt. This is fine.

6) Start another client that you were already using for your encrypted chats previously. In my case this was `app.element.io`, so the rest of the steps here assume that.

7) Run `panctl`. At the prompt, run `start-verification <user ID> <user ID> <Element's device ID>`. `<user ID>` here is the full user ID like `@arnavion:arnavion.dev`. If you only have the one Element session, `panctl` will show you the device ID as an autocomplete hint so you don't have to look it up. If you do need to look it up, go to Element -> profile icon -> All Settings -> Sessions, expand the "Current session" item, and the "Session ID" is the device ID.

8) In Element you will see a popup "Incoming Verification Request". Click "Continue". It will change to a popup containing some emojis, and `panctl` will print the same emojis. Click the "They match" button. It will now change to a popup like "Waiting for other client to confirm..."

9) In `panctl`, run `confirm-verification <user ID> <user ID> <Element's device ID>`, ie the same command as before but with `confirm-verification` instead of `start-verification`.

10) At this point, if you look at all your sessions in Element (profile icon -> All Settings -> Sessions), you should see "pantalaimon" in the "Other sessions" list as a "Verified" session.

11) Export the E2E room keys that Element was using via profile icon -> Security & Privacy -> Export E2E room keys. Pick any password and then save the file to some path.

12) Back in `panctl`, run `import-keys <user ID> <path of file> <password you used to encrypt the file>`. After a few seconds, in the output of `pantalaimon`, you should see a log like `INFO: pantalaimon: Successfully imported keys for <user ID> from <path of file>`.

13) Close and restart the client you had used in step 5, ie the one you want to connect to `pantalaimon`. Now, finally, you should be able to see the encrypted chats be decrypted.

14) Delete the E2E room keys backup file from step 12. You don't need it any more.


15) If in step 11 you had other unverified sessions from pantalaimon from your prior attempts, you can sign out of them too.

You will probably have to repeat steps 11-14 any time you start a new encrypted chat in Element.
16 changes: 8 additions & 8 deletions pantalaimon/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import os
from collections import defaultdict
from pprint import pformat
from typing import Any, Dict, Optional
from urllib.parse import urlparse

from aiohttp.client_exceptions import ClientConnectionError
Expand Down Expand Up @@ -135,7 +134,7 @@ class InvalidLimit(Exception):
class SqliteQStore(SqliteStore):
def _create_database(self):
return SqliteQueueDatabase(
self.database_path, pragmas=(("foregign_keys", 1), ("secure_delete", 1))
self.database_path, pragmas=(("foreign_keys", 1), ("secure_delete", 1))
)

def close(self):
Expand Down Expand Up @@ -554,6 +553,7 @@ def start_loop(self, loop_sleep_time=100):
full_state=True,
since=next_batch,
loop_sleep_time=loop_sleep_time,
set_presence="offline",
)
)
self.task = task
Expand Down Expand Up @@ -708,7 +708,6 @@ async def handle_key_request_message(self, message):
for share in self.get_active_key_requests(
message.user_id, message.device_id
):

continued = True

if not self.continue_key_share(share):
Expand Down Expand Up @@ -810,8 +809,9 @@ def pan_decrypt_event(self, event_dict, room_id=None, ignore_failures=True):

if not isinstance(event, MegolmEvent):
logger.warn(
"Encrypted event is not a megolm event:"
"\n{}".format(pformat(event_dict))
"Encrypted event is not a megolm event:" "\n{}".format(
pformat(event_dict)
)
)
return False

Expand All @@ -835,9 +835,9 @@ def pan_decrypt_event(self, event_dict, room_id=None, ignore_failures=True):
decrypted_event.source["content"]["url"] = decrypted_event.url

if decrypted_event.thumbnail_url:
decrypted_event.source["content"]["info"][
"thumbnail_url"
] = decrypted_event.thumbnail_url
decrypted_event.source["content"]["info"]["thumbnail_url"] = (
decrypted_event.thumbnail_url
)

event_dict.update(decrypted_event.source)
event_dict["decrypted"] = True
Expand Down
1 change: 0 additions & 1 deletion pantalaimon/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ def read(self):

try:
for section_name, section in config.items():

if section_name == "Default":
continue

Expand Down
14 changes: 9 additions & 5 deletions pantalaimon/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ async def _verify_device(self, message_id, client, device):

if ret:
msg = (
f"Device {device.id} of user " f"{device.user_id} successfully verified."
f"Device {device.id} of user "
f"{device.user_id} successfully verified."
)
await client.send_update_device(device)
else:
Expand Down Expand Up @@ -309,7 +310,6 @@ async def receive_message(self, message):
DeviceUnblacklistMessage,
),
):

device = client.device_store[message.user_id].get(message.device_id, None)

if not device:
Expand Down Expand Up @@ -616,7 +616,9 @@ async def start_pan_client(
await pan_client.close()
return

logger.info(f"Successfully started new background sync client for " f"{user_id}")
logger.info(
f"Successfully started new background sync client for " f"{user_id}"
)

await self.send_ui_message(
UpdateUsersMessage(self.name, user_id, pan_client.device_id)
Expand Down Expand Up @@ -733,7 +735,7 @@ async def decrypt_loop(client, body):
return decryption_method(body, ignore_failures=False)
except EncryptionError:
logger.info("Error decrypting sync, waiting for next pan " "sync")
await client.synced.wait(),
(await client.synced.wait(),)
logger.info("Pan synced, retrying decryption.")

try:
Expand Down Expand Up @@ -1294,7 +1296,9 @@ async def _load_decrypted_file(self, server_name, media_id, file_name):
client = next(iter(self.pan_clients.values()))

try:
response = await client.download(server_name, media_id, file_name)
response = await client.download(
server_name=server_name, media_id=media_id, filename=file_name
)
except ClientConnectionError as e:
raise e

Expand Down
2 changes: 0 additions & 2 deletions pantalaimon/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class InvalidQueryError(Exception):
import json
import os
from functools import partial
from typing import Any, Dict, List, Optional, Tuple

import attr
import tantivy
Expand Down Expand Up @@ -230,7 +229,6 @@ def load_events(
)

for message in query:

event = message.event

event_dict = {
Expand Down
1 change: 0 additions & 1 deletion pantalaimon/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import asyncio
import os
import signal
from typing import Optional

import click
import janus
Expand Down
6 changes: 3 additions & 3 deletions pantalaimon/panctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from prompt_toolkit.completion import Completer, Completion, PathCompleter
from prompt_toolkit.document import Document
from prompt_toolkit.patch_stdout import patch_stdout
from pydbus import SessionBus
from dasbus.connection import SessionMessageBus

PTK2 = ptk_version.startswith("2.")

Expand Down Expand Up @@ -404,8 +404,8 @@ class PanCtl:
commands = list(command_help.keys())

def __attrs_post_init__(self):
self.bus = SessionBus()
self.pan_bus = self.bus.get("org.pantalaimon1")
self.bus = SessionMessageBus()
self.pan_bus = self.bus.get_connection("org.pantalaimon1")

self.ctl = self.pan_bus["org.pantalaimon1.control"]
self.devices = self.pan_bus["org.pantalaimon1.devices"]
Expand Down
3 changes: 1 addition & 2 deletions pantalaimon/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import json
import os
from collections import defaultdict
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict

import attr
from nio.crypto import TrustState, GroupSessionStore
Expand Down Expand Up @@ -431,7 +431,6 @@ def load_all_devices(self):
device_store = defaultdict(dict)

for d in account.device_keys:

if d.deleted:
continue

Expand Down
26 changes: 13 additions & 13 deletions pantalaimon/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
UI_ENABLED = (
util.find_spec("gi") is not None
and util.find_spec("gi.repository") is not None
and util.find_spec("pydbus") is not None
and util.find_spec("dasbus") is not None
)

if UI_ENABLED:
Expand All @@ -28,8 +28,8 @@
import dbus
import notify2
from gi.repository import GLib
from pydbus import SessionBus
from pydbus.generic import signal
from dasbus import SessionMessageBus
from dasbus.signal import Signal
from dbus.mainloop.glib import DBusGMainLoop

from nio import RoomKeyRequest, RoomKeyRequestCancellation
Expand Down Expand Up @@ -123,8 +123,8 @@ class Control:
</node>
"""

Response = signal()
UnverifiedDevices = signal()
Response = Signal()
UnverifiedDevices = Signal()

def __init__(self, queue, server_list, id_counter):
self.queue = queue
Expand Down Expand Up @@ -297,13 +297,13 @@ class Devices:
</node>
"""

VerificationInvite = signal()
VerificationCancel = signal()
VerificationString = signal()
VerificationDone = signal()
VerificationInvite = Signal()
VerificationCancel = Signal()
VerificationString = Signal()
VerificationDone = Signal()

KeyRequest = signal()
KeyRequestCancel = signal()
KeyRequest = Signal()
KeyRequestCancel = Signal()

def __init__(self, queue, id_counter):
self.device_list = dict()
Expand Down Expand Up @@ -466,8 +466,8 @@ def __attrs_post_init__(self):
self.control_if = Control(self.send_queue, self.server_list, id_counter)
self.device_if = Devices(self.send_queue, id_counter)

self.bus = SessionBus()
self.bus.publish("org.pantalaimon1", self.control_if, self.device_if)
self.bus = SessionMessageBus()
self.bus.publish_object("org.pantalaimon1", self.control_if, self.device_if)

def unverified_notification(self, message):
notification = notify2.Notification(
Expand Down
15 changes: 8 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
url="https://github.com/matrix-org/pantalaimon",
author="The Matrix.org Team",
author_email="[email protected]",
description=("A Matrix proxy daemon that adds E2E encryption "
"capabilities."),
description=("A Matrix proxy daemon that adds E2E encryption " "capabilities."),
long_description=long_description,
long_description_content_type="text/markdown",
license="Apache License, Version 2.0",
Expand All @@ -29,19 +28,21 @@
"cachetools >= 3.0.0",
"prompt_toolkit > 2, < 4",
"typing;python_version<'3.5'",
"matrix-nio[e2e] >= 0.20, < 0.21"
"matrix-nio[e2e] >= 0.24, < 0.25.2",
],
extras_require={
"ui": [
"dbus-python >= 1.2, < 1.3",
"PyGObject >= 3.36, < 3.39",
"pydbus >= 0.6, < 0.7",
"dasbus == 1.7",
"notify2 >= 0.3, < 0.4",
]
},
entry_points={
"console_scripts": ["pantalaimon=pantalaimon.main:main",
"panctl=pantalaimon.panctl:main"],
"console_scripts": [
"pantalaimon=pantalaimon.main:main",
"panctl=pantalaimon.panctl:main",
],
},
zip_safe=False
zip_safe=False,
)
Loading

0 comments on commit a82652f

Please sign in to comment.