diff --git a/.gitignore b/.gitignore index 9ce117f4..b20ee4e1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ __pycache__ /registration.yaml *.log* *.db +*.pickle *.bak diff --git a/Dockerfile b/Dockerfile index eecf59ed..c7deca05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,26 +30,18 @@ RUN apk add --no-cache \ py3-pysocks \ # cryptg py3-cffi \ + py3-qrcode@edge \ py3-brotli \ # Other dependencies ffmpeg \ ca-certificates \ su-exec \ netcat-openbsd \ - # olm + # encryption olm-dev \ - # matrix-nio? - py3-future \ - py3-atomicwrites \ py3-pycryptodome \ - py3-peewee \ - py3-pyrsistent \ - py3-jsonschema \ - #py3-aiofiles \ # (too new) - py3-cachetools \ py3-unpaddedbase64 \ - py3-h2@edge \ - py3-logbook@edge + py3-future COPY requirements.txt /opt/mautrix-telegram/requirements.txt COPY optional-requirements.txt /opt/mautrix-telegram/optional-requirements.txt diff --git a/alembic/versions/ccbaff858240_switch_to_mautrix_python_crypto.py b/alembic/versions/ccbaff858240_switch_to_mautrix_python_crypto.py new file mode 100644 index 00000000..46d2b274 --- /dev/null +++ b/alembic/versions/ccbaff858240_switch_to_mautrix_python_crypto.py @@ -0,0 +1,71 @@ +"""Switch to mautrix-python crypto + +Revision ID: ccbaff858240 +Revises: 3e3745baa458 +Create Date: 2020-07-08 19:06:12.588047 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'ccbaff858240' +down_revision = '3e3745baa458' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('nio_account') + op.drop_table('nio_device_key') + op.drop_table('nio_outgoing_key_request') + op.drop_table('nio_olm_session') + op.drop_table('nio_megolm_inbound_session') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('nio_megolm_inbound_session', + sa.Column('session_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('sender_key', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('fp_key', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('room_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('session', postgresql.BYTEA(), autoincrement=False, nullable=False), + sa.Column('forwarded_chains', postgresql.BYTEA(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('session_id', name='nio_megolm_inbound_session_pkey') + ) + op.create_table('nio_olm_session', + sa.Column('session_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('sender_key', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('session', postgresql.BYTEA(), autoincrement=False, nullable=False), + sa.Column('created_at', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), + sa.Column('last_used', postgresql.TIMESTAMP(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('session_id', name='nio_olm_session_pkey') + ) + op.create_table('nio_outgoing_key_request', + sa.Column('request_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('session_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('room_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('algorithm', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('request_id', name='nio_outgoing_key_request_pkey') + ) + op.create_table('nio_device_key', + sa.Column('user_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('device_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('display_name', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('deleted', sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.Column('keys', postgresql.BYTEA(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('user_id', 'device_id', name='nio_device_key_pkey') + ) + op.create_table('nio_account', + sa.Column('user_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('device_id', sa.VARCHAR(length=255), autoincrement=False, nullable=False), + sa.Column('shared', sa.BOOLEAN(), autoincrement=False, nullable=False), + sa.Column('sync_token', sa.TEXT(), autoincrement=False, nullable=False), + sa.Column('account', postgresql.BYTEA(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('user_id', 'device_id', name='nio_account_pkey') + ) + # ### end Alembic commands ### diff --git a/mautrix_telegram/config.py b/mautrix_telegram/config.py index 5e20e9a8..4fa36582 100644 --- a/mautrix_telegram/config.py +++ b/mautrix_telegram/config.py @@ -108,6 +108,7 @@ class Config(BaseBridgeConfig): copy("bridge.animated_sticker.args") copy("bridge.encryption.allow") copy("bridge.encryption.default") + copy("bridge.encryption.database") copy("bridge.private_chat_portal_meta") copy("bridge.delivery_receipts") copy("bridge.delivery_error_reports") diff --git a/mautrix_telegram/example-config.yaml b/mautrix_telegram/example-config.yaml index 6aa94fe0..3ec9ed5a 100644 --- a/mautrix_telegram/example-config.yaml +++ b/mautrix_telegram/example-config.yaml @@ -211,6 +211,15 @@ bridge: # Default to encryption, force-enable encryption in all portals the bridge creates # This will cause the bridge bot to be in private chats for the encryption to work properly. default: false + # Database for the encryption data. Currently only supports Postgres and an in-memory + # store that's persisted as a pickle. + # If set to `default`, will use the appservice postgres database + # or a pickle file if the appservice database is sqlite. + # + # Format examples: + # Pickle: pickle://filename.pickle + # Postgres: postgres://username:password@hostname/dbname + database: default # Whether or not to explicitly set the avatar and room name for private # chat portal rooms. This will be implicitly enabled if encryption.default is true. private_chat_portal_meta: false diff --git a/mautrix_telegram/portal/matrix.py b/mautrix_telegram/portal/matrix.py index 118e0ec7..4ea6435c 100644 --- a/mautrix_telegram/portal/matrix.py +++ b/mautrix_telegram/portal/matrix.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: from ..config import Config try: - from nio.crypto import decrypt_attachment + from mautrix.crypto.attachments import decrypt_attachment except ImportError: decrypt_attachment = None diff --git a/mautrix_telegram/portal/metadata.py b/mautrix_telegram/portal/metadata.py index e701b4f1..fd5cc295 100644 --- a/mautrix_telegram/portal/metadata.py +++ b/mautrix_telegram/portal/metadata.py @@ -411,17 +411,13 @@ class PortalMetadata(BasePortal, ABC): if not room_id: raise Exception(f"Failed to create room") - if self.encrypted and self.matrix.e2ee: - members = [self.main_intent.mxid] - if direct: - try: - await self.az.intent.join_room_by_id(room_id) - members += [self.az.intent.mxid] - except Exception: - self.log.warning(f"Failed to add bridge bot to new private chat {room_id}") - await self.matrix.e2ee.add_room(room_id, members=members, encrypted=True) + if self.encrypted and self.matrix.e2ee and direct: + try: + await self.az.intent.ensure_joined(room_id) + except Exception: + self.log.warning(f"Failed to add bridge bot to new private chat {room_id}") - self.mxid = RoomID(room_id) + self.mxid = room_id self.by_mxid[self.mxid] = self self.save() self.az.state_store.set_power_levels(self.mxid, power_levels) diff --git a/mautrix_telegram/util/file_transfer.py b/mautrix_telegram/util/file_transfer.py index 2e0ca8f8..3383c68f 100644 --- a/mautrix_telegram/util/file_transfer.py +++ b/mautrix_telegram/util/file_transfer.py @@ -49,7 +49,7 @@ except ImportError: VideoFileClip = None try: - from nio.crypto import encrypt_attachment + from mautrix.crypto.attachments import encrypt_attachment except ImportError: encrypt_attachment = None diff --git a/mautrix_telegram/util/parallel_file_transfer.py b/mautrix_telegram/util/parallel_file_transfer.py index 0bda98fd..ad9a7f9d 100644 --- a/mautrix_telegram/util/parallel_file_transfer.py +++ b/mautrix_telegram/util/parallel_file_transfer.py @@ -41,7 +41,7 @@ from ..tgclient import MautrixTelegramClient from ..db import TelegramFile as DBTelegramFile try: - from nio.crypto import async_encrypt_attachment + from mautrix.crypto.attachments import async_encrypt_attachment except ImportError: async_encrypt_attachment = None diff --git a/optional-requirements.txt b/optional-requirements.txt index 47d6c6bf..948064d9 100644 --- a/optional-requirements.txt +++ b/optional-requirements.txt @@ -24,4 +24,7 @@ prometheus_client>=0.6,<0.9 psycopg2-binary>=2,<3 #/e2be -matrix-nio[e2e]>=0.9,<0.14 +asyncpg>=0.20,<0.21 +python-olm>=3,<4 +pycryptodome>=3,<4 +unpaddedbase64>=1,<2 diff --git a/requirements.txt b/requirements.txt index 46ae257c..0718236e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,6 @@ ruamel.yaml>=0.15.35,<0.17 python-magic>=0.4,<0.5 commonmark>=0.8,<0.10 aiohttp>=3,<4 -mautrix==0.6.0.alpha4 +mautrix==0.6.0.beta4 telethon>=1.13,<1.16 telethon-session-sqlalchemy>=0.2.14,<0.3